Private/security-functions.ps1
function Check-OrphanedConfigMaps { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[📜 Orphaned ConfigMaps]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Fetching ConfigMaps..." -ForegroundColor Yellow # Exclude Helm-managed ConfigMaps $excludedConfigMapPatterns = @("^sh\.helm\.release\.v1\.") $configMaps = kubectl get configmaps --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items | Where-Object { $_.metadata.name -notmatch ($excludedConfigMapPatterns -join "|") } if ($ExcludeNamespaces) { $configMaps = Exclude-Namespaces -items $configMaps } Write-Host "`r🤖 ✅ ConfigMaps fetched. ($($configMaps.Count) total)" -ForegroundColor Green # Fetch workloads & used ConfigMaps Write-Host -NoNewline "`n🤖 Checking ConfigMap usage..." -ForegroundColor Yellow $usedConfigMaps = @() # Pods $pods = kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items # Various workloads $workloads = @(kubectl get deployments --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get statefulsets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get daemonsets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get cronjobs --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get jobs --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get replicasets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) $ingresses = kubectl get ingress --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $services = kubectl get services --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items # Scan Pods + workloads for configmap references foreach ($resource in $pods + $workloads) { $usedConfigMaps += $resource.spec.volumes | Where-Object { $_.configMap } | Select-Object -ExpandProperty configMap | Select-Object -ExpandProperty name foreach ($container in $resource.spec.containers) { if ($container.env) { $usedConfigMaps += $container.env | Where-Object { $_.valueFrom.configMapKeyRef } | Select-Object -ExpandProperty valueFrom | Select-Object -ExpandProperty configMapKeyRef | Select-Object -ExpandProperty name } if ($container.envFrom) { $usedConfigMaps += $container.envFrom | Where-Object { $_.configMapRef } | Select-Object -ExpandProperty configMapRef | Select-Object -ExpandProperty name } } } # Ingress & Service annotations $usedConfigMaps += $ingresses | ForEach-Object { $_.metadata.annotations.Values -match "configMap" } $usedConfigMaps += $services | ForEach-Object { $_.metadata.annotations.Values -match "configMap" } # Custom Resources $crds = kubectl get crds -o json | ConvertFrom-Json | Select-Object -ExpandProperty items foreach ($crd in $crds) { $crdKind = $crd.spec.names.kind if ($crdKind -match "^[a-z0-9-]+$") { $customResources = kubectl get $crdKind --all-namespaces -o json 2>$null | ConvertFrom-Json | Select-Object -ExpandProperty items foreach ($cr in $customResources) { if ($cr.metadata.annotations.Values -match "configMap") { $usedConfigMaps += $cr.metadata.annotations.Values } } } } # Clean up references $usedConfigMaps = $usedConfigMaps | Where-Object { $_ } | Sort-Object -Unique Write-Host "`r✅ ConfigMap usage checked. " -ForegroundColor Green # Orphaned = not in usedConfigMaps $orphanedConfigMaps = $configMaps | Where-Object { $_.metadata.name -notin $usedConfigMaps } # Build an array for pagination / output $orphanedItems = @() foreach ($ocm in $orphanedConfigMaps) { $orphanedItems += [PSCustomObject]@{ Namespace = $ocm.metadata.namespace Type = "📜 ConfigMap" Name = $ocm.metadata.name } } if ($orphanedItems.Count -eq 0) { Write-Host "🤖 ✅ No orphaned ConfigMaps found." -ForegroundColor Green if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[📜 Orphaned ConfigMaps]`n" Write-ToReport "✅ No orphaned ConfigMaps found." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } # If -Html is specified, create & return an HTML table if ($Html) { $htmlTable = $orphanedItems | ConvertTo-Html -Fragment -Property Namespace, Type, Name -PreContent "<h2>Orphaned ConfigMaps</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total Orphaned ConfigMaps Found:</strong> $($orphanedItems.Count)</p>" + $htmlTable return $htmlTable } # If in report mode, ASCII if ($Global:MakeReport) { Write-ToReport "`n[📜 Orphaned ConfigMaps]`n" Write-ToReport "⚠️ Total Orphaned ConfigMaps Found: $($orphanedItems.Count)" $tableString = $orphanedItems | Format-Table Namespace, Type, Name -AutoSize | Out-String Write-ToReport $tableString return } # Pagination $totalItems = $orphanedItems.Count $currentPage = 0 $totalPages = [math]::Ceiling($totalItems / $PageSize) do { Clear-Host Write-Host "`n[📜 Orphaned ConfigMaps - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 ConfigMaps store configuration data for workloads.", "", "📌 This check identifies ConfigMaps that are not referenced by:", " - Pods, Deployments, StatefulSets, DaemonSets.", " - CronJobs, Jobs, ReplicaSets, Services, and Custom Resources.", "", "⚠️ Orphaned ConfigMaps may be outdated and can be reviewed for cleanup.", "", "⚠️ Total Orphaned ConfigMaps Found: $($orphanedItems.Count)" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 # first page only } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $totalItems) $tableData = $orphanedItems[$startIndex..($endIndex - 1)] if ($tableData) { $tableData | Format-Table Namespace, Type, Name -AutoSize | Out-Host } $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } function Check-OrphanedSecrets { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[🔑 Orphaned Secrets]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Fetching Secrets..." -ForegroundColor Yellow # Exclude system-managed secrets $excludedSecretPatterns = @("^sh\.helm\.release\.v1\.", "^bootstrap-token-", "^default-token-", "^kube-root-ca.crt$", "^kubernetes.io/service-account-token") $secrets = kubectl get secrets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items | Where-Object { $_.metadata.name -notmatch ($excludedSecretPatterns -join "|") } if ($ExcludeNamespaces) { $secrets = Exclude-Namespaces -items $secrets } Write-Host "`r🤖 ✅ Secrets fetched. ($($secrets.Count) total)" -ForegroundColor Green Write-Host -NoNewline "`n🤖 Checking Secret usage..." -ForegroundColor Yellow $usedSecrets = @() # Pods and various workloads $pods = kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $workloads = @(kubectl get deployments --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get statefulsets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) + @(kubectl get daemonsets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items) $ingresses = kubectl get ingress --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $serviceAccounts = kubectl get serviceaccounts --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items foreach ($resource in $pods + $workloads) { $usedSecrets += $resource.spec.volumes | Where-Object { $_.secret } | Select-Object -ExpandProperty secret | Select-Object -ExpandProperty secretName foreach ($container in $resource.spec.containers) { if ($container.env) { $usedSecrets += $container.env | Where-Object { $_.valueFrom.secretKeyRef } | Select-Object -ExpandProperty valueFrom | Select-Object -ExpandProperty secretKeyRef | Select-Object -ExpandProperty name } } } # Ingress TLS $usedSecrets += $ingresses | ForEach-Object { if ($_.spec.tls) { $_.spec.tls | Where-Object { $_.secretName } | Select-Object -ExpandProperty secretName } } # ServiceAccounts $usedSecrets += $serviceAccounts | ForEach-Object { $_.secrets | Select-Object -ExpandProperty name } # Check custom resources $customResources = kubectl api-resources --verbs=list --namespaced -o name | Where-Object { $_ } foreach ($cr in $customResources) { $crInstances = kubectl get $cr --all-namespaces -o json 2>$null | ConvertFrom-Json | Select-Object -ExpandProperty items if ($crInstances) { foreach ($instance in $crInstances) { if ($instance.spec -and $instance.spec.PSObject.Properties.name -contains "secretName") { $usedSecrets += $instance.spec.secretName } } } } $usedSecrets = $usedSecrets | Where-Object { $_ } | Sort-Object -Unique Write-Host "`r🤖 ✅ Secret usage checked. ($($usedSecrets.Count) in use) " -ForegroundColor Green # Orphaned Secrets $orphanedSecrets = $secrets | Where-Object { $_.metadata.name -notin $usedSecrets } $orphanedItems = @() foreach ($sec in $orphanedSecrets) { $orphanedItems += [PSCustomObject]@{ Namespace = $sec.metadata.namespace Type = "🔑 Secret" Name = $sec.metadata.name } } if ($orphanedItems.Count -eq 0) { Write-Host "🤖 ✅ No orphaned Secrets found." -ForegroundColor Green if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[🔑 Orphaned Secrets]`n" Write-ToReport "✅ No orphaned Secrets found." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } # If -Html if ($Html) { $htmlTable = $orphanedItems | ConvertTo-Html -Fragment -Property Namespace, Type, Name -PreContent "<h2>Orphaned Secrets</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total Orphaned Secrets Found:</strong> $($orphanedItems.Count)</p>" + $htmlTable return $htmlTable } # If in report mode if ($Global:MakeReport) { Write-ToReport "`n[🔑 Orphaned Secrets]`n" Write-ToReport "⚠️ Total Orphaned Secrets Found: $($orphanedItems.Count)" $tableString = $orphanedItems | Format-Table Namespace, Type, Name -AutoSize | Out-String Write-ToReport $tableString return } # Pagination $totalItems = $orphanedItems.Count $currentPage = 0 $totalPages = [math]::Ceiling($totalItems / $PageSize) do { Clear-Host Write-Host "`n[🔑 Orphaned Secrets - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 Secrets store sensitive data such as API keys and credentials.", "", "📌 This check identifies Secrets that are NOT used by:", " - Pods, Deployments, StatefulSets, DaemonSets.", " - Ingress TLS, ServiceAccounts, and Custom Resources.", "", "⚠️ Unused Secrets may indicate outdated credentials or misconfigurations.", "", "⚠️ Total Orphaned Secrets Found: $($orphanedItems.Count)" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 # first page only } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $totalItems) $tableData = $orphanedItems[$startIndex..($endIndex - 1)] if ($tableData) { $tableData | Format-Table Namespace, Type, Name -AutoSize | Out-Host } $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } function Check-RBACOverexposure { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[🔓 RBAC Overexposure Check]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Analyzing Roles and Bindings..." -ForegroundColor Yellow $findings = @() # Get all roles and cluster roles $roles = kubectl get roles --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $clusterRoles = kubectl get clusterroles -o json | ConvertFrom-Json | Select-Object -ExpandProperty items if ($ExcludeNamespaces) { $roles = Exclude-Namespaces -items $roles } # Get bindings $roleBindings = kubectl get rolebindings --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $clusterRoleBindings = kubectl get clusterrolebindings -o json | ConvertFrom-Json | Select-Object -ExpandProperty items if ($ExcludeNamespaces) { $roleBindings = Exclude-Namespaces -items $roleBindings } # Find risky roles $wildcardRoles = @{} foreach ($cr in $clusterRoles) { foreach ($rule in $cr.rules) { if ($rule.verbs -contains "*" -and $rule.resources -contains "*" -and $rule.apiGroups -contains "*") { $wildcardRoles[$cr.metadata.name] = "ClusterRole" break } } } foreach ($r in $roles) { foreach ($rule in $r.rules) { if ($rule.verbs -contains "*" -and $rule.resources -contains "*" -and $rule.apiGroups -contains "*") { $wildcardRoles["$($r.metadata.namespace)/$($r.metadata.name)"] = "Role" break } } } # Check ClusterRoleBindings foreach ($crb in $clusterRoleBindings) { $roleName = $crb.roleRef.name $isClusterAdmin = ($roleName -eq "cluster-admin") $isWildcard = $wildcardRoles.ContainsKey($roleName) if ($isClusterAdmin -or $isWildcard) { foreach ($subject in $crb.subjects) { $findings += [PSCustomObject]@{ Namespace = "🌍 Cluster-Wide" Binding = $crb.metadata.name Subject = "$($subject.kind)/$($subject.name)" Role = $roleName Scope = "ClusterRoleBinding" Risk = if ($isClusterAdmin) { "❗ cluster-admin" } else { "⚠️ wildcard access" } } } } } # Check RoleBindings foreach ($rb in $roleBindings) { $roleName = $rb.roleRef.name $ns = $rb.metadata.namespace $key = "$ns/$roleName" $isClusterAdmin = ($roleName -eq "cluster-admin") $isWildcard = $wildcardRoles.ContainsKey($key) if ($isClusterAdmin -or $isWildcard) { foreach ($subject in $rb.subjects) { $findings += [PSCustomObject]@{ Namespace = $ns Binding = $rb.metadata.name Subject = "$($subject.kind)/$($subject.name)" Role = $roleName Scope = "RoleBinding" Risk = if ($isClusterAdmin) { "❗ cluster-admin" } else { "⚠️ wildcard access" } } } } } $total = $findings.Count Write-Host "`r🤖 ✅ Check complete. ($total high-risk bindings found)" -ForegroundColor Green if ($total -eq 0) { Write-Host "✅ No overexposed roles or bindings found." -ForegroundColor Green if ($Html) { return "<p><strong>✅ No RBAC overexposure detected.</strong></p>" } if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[🔓 RBAC Overexposure Check]`n" Write-ToReport "✅ No cluster-admin or wildcard access detected." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } if ($Html) { $htmlTable = $findings | ConvertTo-Html -Fragment -Property Namespace, Binding, Subject, Role, Scope, Risk -PreContent "<h2>RBAC Overexposure (cluster-admin or wildcard)</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total Overexposed Bindings:</strong> $total</p>" + $htmlTable return $htmlTable } if ($Global:MakeReport) { Write-ToReport "`n[🔓 RBAC Overexposure Check]`n" Write-ToReport "⚠️ Total Overexposed Bindings: $total" $tableString = $findings | Format-Table Namespace, Binding, Subject, Role, Scope, Risk -AutoSize | Out-String Write-ToReport $tableString return } # Pagination $currentPage = 0 $totalPages = [math]::Ceiling($total / $PageSize) do { Clear-Host Write-Host "`n[🔓 RBAC Overexposure - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 This check identifies risky access via RBAC.", "", "📌 Included:", " - cluster-admin grants (direct bindings)", " - Custom Roles with * verbs, * resources, * apiGroups", "", "⚠️ These bindings may allow full control over your cluster.", "", "⚠️ Total Overexposed Bindings Found: $total" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $total) $pageData = $findings[$startIndex..($endIndex - 1)] $pageData | Format-Table Namespace, Binding, Subject, Role, Scope, Risk -AutoSize | Out-Host $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } function Check-RBACMisconfigurations { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[RBAC Misconfigurations]" -ForegroundColor Cyan # Fetch RoleBindings & ClusterRoleBindings Write-Host -NoNewline "`n🤖 Fetching RoleBindings & ClusterRoleBindings..." -ForegroundColor Yellow $roleBindings = kubectl get rolebindings --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $clusterRoleBindings = kubectl get clusterrolebindings -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $roles = kubectl get roles --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $clusterRoles = kubectl get clusterroles -o json | ConvertFrom-Json | Select-Object -ExpandProperty items $existingNamespaces = kubectl get namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items | Select-Object -ExpandProperty metadata | Select-Object -ExpandProperty name if ($ExcludeNamespaces) { $roleBindings = Exclude-Namespaces -items $roleBindings } if ($ExcludeNamespaces) { $roles = Exclude-Namespaces -items $roles } Write-Host "`r🤖 ✅ Fetched $($roleBindings.Count) RoleBindings, $($clusterRoleBindings.Count) ClusterRoleBindings.`n" -ForegroundColor Green $invalidRBAC = @() Write-Host -NoNewline "🤖 Analyzing RBAC configurations..." -ForegroundColor Yellow # Evaluate RoleBindings foreach ($rb in $roleBindings) { $rbNamespace = $rb.metadata.namespace $namespaceExists = $rbNamespace -in $existingNamespaces # Check if the role exists in that namespace $roleExists = $roles | Where-Object { $_.metadata.name -eq $rb.roleRef.name -and $_.metadata.namespace -eq $rbNamespace } if (-not $roleExists -and $rb.roleRef.kind -eq "Role") { $invalidRBAC += [PSCustomObject]@{ Namespace = if ($namespaceExists) { $rbNamespace } else { "🛑 Namespace Missing" } Type = "🔹 Namespace Role" RoleBinding = $rb.metadata.name Subject = "N/A" Issue = "❌ Missing Role: $($rb.roleRef.name)" } } # For RoleRef kind = "ClusterRole", you could check $clusterRoles if needed # Check each subject foreach ($subject in $rb.subjects) { if ($subject.kind -eq "ServiceAccount") { if (-not $namespaceExists) { $invalidRBAC += [PSCustomObject]@{ Namespace = "🛑 Namespace Missing" Type = "🔹 Namespace Role" RoleBinding = $rb.metadata.name Subject = "$($subject.kind)/$($subject.name)" Issue = "🛑 Namespace does not exist" } } else { $exists = kubectl get serviceaccount -n $subject.namespace $subject.name -o json 2>$null if (-not $exists) { $invalidRBAC += [PSCustomObject]@{ Namespace = $rbNamespace Type = "🔹 Namespace Role" RoleBinding = $rb.metadata.name Subject = "$($subject.kind)/$($subject.name)" Issue = "❌ ServiceAccount does not exist" } } } } } } # Evaluate ClusterRoleBindings foreach ($crb in $clusterRoleBindings) { foreach ($subject in $crb.subjects) { if ($subject.kind -eq "ServiceAccount") { if ($subject.namespace -notin $existingNamespaces) { $invalidRBAC += [PSCustomObject]@{ Namespace = "🛑 Namespace Missing" Type = "🔸 Cluster Role" RoleBinding = $crb.metadata.name Subject = "$($subject.kind)/$($subject.name)" Issue = "🛑 Namespace does not exist" } } else { $exists = kubectl get serviceaccount -n $subject.namespace $subject.name -o json 2>$null if (-not $exists) { $invalidRBAC += [PSCustomObject]@{ Namespace = "🌍 Cluster-Wide" Type = "🔸 Cluster Role" RoleBinding = $crb.metadata.name Subject = "$($subject.kind)/$($subject.name)" Issue = "❌ ServiceAccount does not exist" } } } } } } write-host "`r🤖 ✅ RBAC configurations Checked. " -ForegroundColor Green if ($invalidRBAC.Count -eq 0) { Write-Host "`r✅ No RBAC misconfigurations found." -ForegroundColor Green if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[RBAC Misconfigurations]`n" Write-ToReport "✅ No RBAC misconfigurations found." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } # If -Html, build an HTML table if ($Html) { if ($invalidRBAC.Count -eq 0) { return "<p><strong>✅ No RBAC misconfigurations found.</strong></p>" } $htmlTable = $invalidRBAC | ConvertTo-Html -Fragment -Property Namespace, Type, RoleBinding, Subject, Issue -PreContent "<h2>RBAC Misconfigurations</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total RBAC Misconfigurations Detected:</strong> $($invalidRBAC.Count)</p>" + $htmlTable return $htmlTable } # If in report mode if ($Global:MakeReport) { Write-ToReport "`n[RBAC Misconfigurations]`n" Write-ToReport "⚠️ Total RBAC Misconfigurations Detected: $($invalidRBAC.Count)" $tableString = $invalidRBAC | Format-Table Namespace, Type, RoleBinding, Subject, Issue -AutoSize | Out-String Write-ToReport $tableString return } # Otherwise, do pagination $totalBindings = $invalidRBAC.Count $currentPage = 0 $totalPages = [math]::Ceiling($totalBindings / $PageSize) do { Clear-Host Write-Host "`n[RBAC Misconfigurations - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 RBAC (Role-Based Access Control) defines who can do what in your cluster.", "", "📌 This check identifies:", " - 🔍 Misconfigurations in RoleBindings & ClusterRoleBindings.", " - ❌ Missing references to ServiceAccounts & Namespaces.", " - 🔓 Overly permissive roles that may pose security risks.", "", "⚠️ Total RBAC Misconfigurations Detected: $totalBindings" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 # first page only } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $totalBindings) $tableData = $invalidRBAC[$startIndex..($endIndex - 1)] if ($tableData) { $tableData | Format-Table Namespace, Type, RoleBinding, Subject, Issue -AutoSize | Out-Host } $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } function Check-HostPidAndNetwork { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[🔌 Pods with hostPID / hostNetwork]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Fetching Pods..." -ForegroundColor Yellow $pods = kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items if ($ExcludeNamespaces) { $pods = Exclude-Namespaces -items $pods } Write-Host "`r🤖 ✅ Pods fetched. ($($pods.Count) total)" -ForegroundColor Green Write-Host -NoNewline "`n🤖 Scanning for hostPID / hostNetwork usage..." -ForegroundColor Yellow $flaggedPods = @() foreach ($pod in $pods) { $hostPID = $pod.spec.hostPID $hostNetwork = $pod.spec.hostNetwork if ($hostPID -or $hostNetwork) { $flaggedPods += [PSCustomObject]@{ Namespace = $pod.metadata.namespace Pod = $pod.metadata.name hostPID = if ($hostPID-eq "true") { "❌ true" } else { "✅ false" } hostNetwork = if ($hostNetwork -eq "true") { "❌ true" } else { "✅ false" } } } } Write-Host "`r🤖 ✅ Scan complete. ($($flaggedPods.Count) flagged) " -ForegroundColor Green if ($flaggedPods.Count -eq 0) { Write-Host "✅ No pods with hostPID or hostNetwork found." -ForegroundColor Green if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[🔌 Pods with hostPID / hostNetwork]`n" Write-ToReport "✅ No pods with hostPID or hostNetwork found." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } if ($Html) { if ($flaggedPods.Count -eq 0) { return "<p><strong>✅ No pods with hostPID or hostNetwork found.</strong></p>" } $htmlTable = $flaggedPods | ConvertTo-Html -Fragment -Property Namespace, Pod, hostPID, hostNetwork -PreContent "<h2>Pods with hostPID / hostNetwork</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total Flagged Pods:</strong> $($flaggedPods.Count)</p>" + $htmlTable return $htmlTable } if ($Global:MakeReport) { Write-ToReport "`n[🔌 Pods with hostPID / hostNetwork]`n" Write-ToReport "⚠️ Total Flagged Pods: $($flaggedPods.Count)" $tableString = $flaggedPods | Format-Table Namespace, Pod, hostPID, hostNetwork -AutoSize | Out-String Write-ToReport $tableString return } # Pagination $totalItems = $flaggedPods.Count $currentPage = 0 $totalPages = [math]::Ceiling($totalItems / $PageSize) do { Clear-Host Write-Host "`n[🔌 Pods with hostPID / hostNetwork - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 Some pods use host-level process or network namespaces.", "", "📌 This check identifies pods with:", " - hostPID = true", " - hostNetwork = true", "", "⚠️ These settings can bypass isolation and expose the node.", "", "⚠️ Total Flagged Pods: $($flaggedPods.Count)" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $totalItems) $tableData = $flaggedPods[$startIndex..($endIndex - 1)] if ($tableData) { $tableData | Format-Table Namespace, Pod, hostPID, hostNetwork -AutoSize | Out-Host } $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } function Check-PodsRunningAsRoot { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[👑 Pods Running as Root]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Fetching Pods..." -ForegroundColor Yellow $pods = kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items if ($ExcludeNamespaces) { $pods = Exclude-Namespaces -items $pods } Write-Host "`r🤖 ✅ Pods fetched. ($($pods.Count) total)" -ForegroundColor Green Write-Host -NoNewline "`n🤖 Scanning for root user usage..." -ForegroundColor Yellow $rootPods = @() foreach ($pod in $pods) { $podUser = $pod.spec.securityContext.runAsUser foreach ($container in $pod.spec.containers) { $containerUser = $container.securityContext.runAsUser $isRoot = -not $containerUser -and -not $podUser if (($containerUser -eq 0) -or ($podUser -eq 0) -or $isRoot) { $rootPods += [PSCustomObject]@{ Namespace = $pod.metadata.namespace Pod = $pod.metadata.name Container = $container.name runAsUser = if ($containerUser) { $containerUser } elseif ($podUser) { $podUser } else { "Not Set (Defaults to root)" } } } } } Write-Host "`r🤖 ✅ Scan complete. ($($rootPods.Count) flagged) " -ForegroundColor Green if ($rootPods.Count -eq 0) { Write-Host "✅ No pods running as root." -ForegroundColor Green if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[👑 Pods Running as Root]`n" Write-ToReport "✅ No pods running as root." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } if ($Html) { if ($rootPods.Count -eq 0) { return "<p><strong>✅ No pods running as root.</strong></p>" } $htmlTable = $rootPods | ConvertTo-Html -Fragment -Property Namespace, Pod, Container, runAsUser -PreContent "<h2>Pods Running as Root</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total Pods Running as Root:</strong> $($rootPods.Count)</p>" + $htmlTable return $htmlTable } if ($Global:MakeReport) { Write-ToReport "`n[👑 Pods Running as Root]`n" Write-ToReport "⚠️ Total Pods Running as Root: $($rootPods.Count)" $tableString = $rootPods | Format-Table Namespace, Pod, Container, runAsUser -AutoSize | Out-String Write-ToReport $tableString return } # Pagination $totalItems = $rootPods.Count $currentPage = 0 $totalPages = [math]::Ceiling($totalItems / $PageSize) do { Clear-Host Write-Host "`n[👑 Pods Running as Root - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 Some pods are running as root (UID 0) or without explicit user settings.", "", "📌 This check looks for:", " - container or pod runAsUser = 0", " - runAsUser not set (defaults to root)", "", "⚠️ Running as root bypasses container security boundaries.", "", "⚠️ Total Flagged Pods: $($rootPods.Count)" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $totalItems) $tableData = $rootPods[$startIndex..($endIndex - 1)] if ($tableData) { $tableData | Format-Table Namespace, Pod, Container, runAsUser -AutoSize | Out-Host } $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } function Check-PrivilegedContainers { param( [int]$PageSize = 10, [switch]$Html, [switch]$ExcludeNamespaces ) if (-not $Global:MakeReport -and -not $Html) { Clear-Host } Write-Host "`n[🔓 Privileged Containers]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Fetching Pods..." -ForegroundColor Yellow $pods = kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items if ($ExcludeNamespaces) { $pods = Exclude-Namespaces -items $pods } Write-Host "`r🤖 ✅ Pods fetched. ($($pods.Count) total)" -ForegroundColor Green Write-Host -NoNewline "`n🤖 Scanning for privileged containers..." -ForegroundColor Yellow $privileged = @() foreach ($pod in $pods) { foreach ($container in $pod.spec.containers) { $isPrivileged = $container.securityContext.privileged if ($isPrivileged) { $privileged += [PSCustomObject]@{ Namespace = $pod.metadata.namespace Pod = $pod.metadata.name Container = $container.name } } } } Write-Host "`r🤖 ✅ Scan complete. ($($privileged.Count) flagged) " -ForegroundColor Green if ($privileged.Count -eq 0) { Write-Host "✅ No privileged containers found." -ForegroundColor Green if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[🔓 Privileged Containers]`n" Write-ToReport "✅ No privileged containers found." } if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" } return } if ($Html) { if ($privileged.Count -eq 0) { return "<p><strong>✅ No privileged containers found.</strong></p>" } $htmlTable = $privileged | ConvertTo-Html -Fragment -Property Namespace, Pod, Container -PreContent "<h2>Privileged Containers</h2>" | Out-String $htmlTable = "<p><strong>⚠️ Total Privileged Containers Found:</strong> $($privileged.Count)</p>" + $htmlTable return $htmlTable } if ($Global:MakeReport) { Write-ToReport "`n[🔓 Privileged Containers]`n" Write-ToReport "⚠️ Total Privileged Containers Found: $($privileged.Count)" $tableString = $privileged | Format-Table Namespace, Pod, Container -AutoSize | Out-String Write-ToReport $tableString return } # Pagination $totalItems = $privileged.Count $currentPage = 0 $totalPages = [math]::Ceiling($totalItems / $PageSize) do { Clear-Host Write-Host "`n[🔓 Privileged Containers - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan $msg = @( "🤖 Privileged containers run with extended access to the host.", "", "📌 This check flags containers where:", " - securityContext.privileged = true", "", "⚠️ This setting grants broad capabilities and should be avoided.", "", "⚠️ Total Privileged Containers Found: $($privileged.Count)" ) if ($currentPage -eq 0) { Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 } $startIndex = $currentPage * $PageSize $endIndex = [math]::Min($startIndex + $PageSize, $totalItems) $tableData = $privileged[$startIndex..($endIndex - 1)] if ($tableData) { $tableData | Format-Table Namespace, Pod, Container -AutoSize | Out-Host } $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages if ($newPage -eq -1) { break } $currentPage = $newPage } while ($true) } |