mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
fix(kubernetes): block kubeconfig command auth bypass
- Reject legacy auth-provider cmd-path command authentication - Keep backend validation as the Cloud security boundary - Cover API and UI validation with regression tests
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Kubernetes kubeconfig validation now rejects legacy `auth-provider.config.cmd-path` command authentication in Prowler Cloud/API
|
||||
@@ -309,6 +309,36 @@ current-context: test-context
|
||||
assert not serializer.is_valid()
|
||||
assert "kubeconfig_content" in serializer.errors
|
||||
|
||||
def test_kubeconfig_with_auth_provider_cmd_path_is_rejected(self):
|
||||
kubeconfig_content = """
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: test-cluster
|
||||
cluster:
|
||||
server: https://kubernetes.example.test
|
||||
users:
|
||||
- name: test-user
|
||||
user:
|
||||
auth-provider:
|
||||
name: gcp
|
||||
config:
|
||||
cmd-path: /bin/sh
|
||||
contexts:
|
||||
- name: test-context
|
||||
context:
|
||||
cluster: test-cluster
|
||||
user: test-user
|
||||
current-context: test-context
|
||||
"""
|
||||
|
||||
serializer = KubernetesProviderSecret(
|
||||
data={"kubeconfig_content": kubeconfig_content}
|
||||
)
|
||||
|
||||
assert not serializer.is_valid()
|
||||
assert "kubeconfig_content" in serializer.errors
|
||||
|
||||
def test_malformed_kubeconfig_is_rejected(self):
|
||||
serializer = KubernetesProviderSecret(
|
||||
data={"kubeconfig_content": "apiVersion: ["}
|
||||
|
||||
@@ -214,7 +214,7 @@ from rest_framework_json_api import serializers
|
||||
"kubeconfig_content": {
|
||||
"type": "string",
|
||||
"description": "The content of the Kubernetes kubeconfig file, encoded as a string. "
|
||||
"Kubeconfig exec authentication is not supported in Prowler Cloud for security reasons.",
|
||||
"Kubeconfig command-based authentication is not supported in Prowler Cloud for security reasons.",
|
||||
}
|
||||
},
|
||||
"required": ["kubeconfig_content"],
|
||||
|
||||
@@ -1569,14 +1569,14 @@ class FindingMetadataSerializer(BaseSerializerV1):
|
||||
|
||||
|
||||
# Provider secrets
|
||||
KUBERNETES_KUBECONFIG_EXEC_ERROR = (
|
||||
"Kubernetes kubeconfig exec authentication is not supported in Prowler Cloud "
|
||||
"for security reasons."
|
||||
KUBERNETES_KUBECONFIG_UNSUPPORTED_COMMAND_AUTH_ERROR = (
|
||||
"Kubernetes kubeconfig command-based authentication is not supported in "
|
||||
"Prowler Cloud for security reasons."
|
||||
)
|
||||
KUBERNETES_KUBECONFIG_INVALID_ERROR = "Invalid Kubernetes kubeconfig content."
|
||||
|
||||
|
||||
def kubeconfig_contains_exec_auth(kubeconfig: dict) -> bool:
|
||||
def kubeconfig_contains_unsupported_command_auth(kubeconfig: dict) -> bool:
|
||||
users = kubeconfig.get("users", [])
|
||||
if not isinstance(users, list):
|
||||
raise ValidationError(KUBERNETES_KUBECONFIG_INVALID_ERROR)
|
||||
@@ -1592,6 +1592,17 @@ def kubeconfig_contains_exec_auth(kubeconfig: dict) -> bool:
|
||||
if "exec" in user:
|
||||
return True
|
||||
|
||||
auth_provider = user.get("auth-provider", {})
|
||||
if not isinstance(auth_provider, dict):
|
||||
continue
|
||||
|
||||
auth_provider_config = auth_provider.get("config", {})
|
||||
if not isinstance(auth_provider_config, dict):
|
||||
continue
|
||||
|
||||
if "cmd-path" in auth_provider_config:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@@ -1788,8 +1799,10 @@ class KubernetesProviderSecret(serializers.Serializer):
|
||||
if not isinstance(kubeconfig, dict):
|
||||
raise serializers.ValidationError(KUBERNETES_KUBECONFIG_INVALID_ERROR)
|
||||
|
||||
if kubeconfig_contains_exec_auth(kubeconfig):
|
||||
raise serializers.ValidationError(KUBERNETES_KUBECONFIG_EXEC_ERROR)
|
||||
if kubeconfig_contains_unsupported_command_auth(kubeconfig):
|
||||
raise serializers.ValidationError(
|
||||
KUBERNETES_KUBECONFIG_UNSUPPORTED_COMMAND_AUTH_ERROR
|
||||
)
|
||||
|
||||
return kubeconfig_content
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Kubernetes credential forms now reject kubeconfig files using legacy `auth-provider.config.cmd-path` command authentication
|
||||
@@ -5,8 +5,8 @@ import { Control, useWatch } from "react-hook-form";
|
||||
import { WizardTextareaField } from "@/components/providers/workflow/forms/fields";
|
||||
import { KubernetesCredentials } from "@/types";
|
||||
import {
|
||||
KUBECONFIG_EXEC_AUTHENTICATION_ERROR,
|
||||
kubeconfigContainsExecAuthentication,
|
||||
KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR,
|
||||
kubeconfigContainsUnsupportedCommandAuthentication,
|
||||
} from "@/types/formSchemas";
|
||||
|
||||
export const KubernetesCredentialsForm = ({
|
||||
@@ -18,9 +18,8 @@ export const KubernetesCredentialsForm = ({
|
||||
control,
|
||||
name: "kubeconfig_content",
|
||||
});
|
||||
const hasExecAuthentication = kubeconfigContainsExecAuthentication(
|
||||
kubeconfigContent ?? "",
|
||||
);
|
||||
const hasUnsupportedCommandAuthentication =
|
||||
kubeconfigContainsUnsupportedCommandAuthentication(kubeconfigContent ?? "");
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -42,9 +41,9 @@ export const KubernetesCredentialsForm = ({
|
||||
minRows={10}
|
||||
isRequired
|
||||
/>
|
||||
{hasExecAuthentication && (
|
||||
{hasUnsupportedCommandAuthentication && (
|
||||
<p className="text-text-error-primary text-xs">
|
||||
{KUBECONFIG_EXEC_AUTHENTICATION_ERROR}
|
||||
{KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -198,6 +198,32 @@ users:
|
||||
);
|
||||
});
|
||||
|
||||
it("reports kubeconfig auth-provider cmd-path on kubeconfig_content field", () => {
|
||||
const schema = addCredentialsFormSchema("kubernetes");
|
||||
|
||||
const result = schema.safeParse({
|
||||
...BASE_KUBERNETES_VALUES,
|
||||
[ProviderCredentialFields.KUBECONFIG_CONTENT]: `apiVersion: v1
|
||||
kind: Config
|
||||
users:
|
||||
- name: test-user
|
||||
user:
|
||||
auth-provider:
|
||||
name: gcp
|
||||
config:
|
||||
cmd-path: /bin/sh`,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (result.success) return;
|
||||
|
||||
expect(result.error.issues).toContainEqual(
|
||||
expect.objectContaining({
|
||||
path: [ProviderCredentialFields.KUBECONFIG_CONTENT],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts malformed kubeconfig content for backend validation", () => {
|
||||
const schema = addCredentialsFormSchema("kubernetes");
|
||||
|
||||
|
||||
+19
-6
@@ -6,14 +6,14 @@ import { validateMutelistYaml, validateYaml } from "@/lib/yaml";
|
||||
|
||||
import { PROVIDER_TYPES, ProviderType } from "./providers";
|
||||
|
||||
export const KUBECONFIG_EXEC_AUTHENTICATION_ERROR =
|
||||
"Kubernetes kubeconfig exec authentication is not supported in Prowler Cloud for security reasons.";
|
||||
export const KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR =
|
||||
"Kubernetes kubeconfig command-based authentication is not supported in Prowler Cloud for security reasons.";
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> => {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
};
|
||||
|
||||
export const kubeconfigContainsExecAuthentication = (
|
||||
export const kubeconfigContainsUnsupportedCommandAuthentication = (
|
||||
value: string,
|
||||
): boolean => {
|
||||
try {
|
||||
@@ -28,7 +28,16 @@ export const kubeconfigContainsExecAuthentication = (
|
||||
return false;
|
||||
}
|
||||
|
||||
return "exec" in userEntry.user;
|
||||
if ("exec" in userEntry.user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const authProvider = userEntry.user["auth-provider"];
|
||||
if (!isRecord(authProvider) || !isRecord(authProvider.config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return "cmd-path" in authProvider.config;
|
||||
});
|
||||
} catch {
|
||||
return false;
|
||||
@@ -229,9 +238,13 @@ export const addCredentialsFormSchema = (
|
||||
.string()
|
||||
.min(1, "Kubeconfig Content is required")
|
||||
.refine(
|
||||
(value) => !kubeconfigContainsExecAuthentication(value),
|
||||
(value) =>
|
||||
!kubeconfigContainsUnsupportedCommandAuthentication(
|
||||
value,
|
||||
),
|
||||
{
|
||||
error: KUBECONFIG_EXEC_AUTHENTICATION_ERROR,
|
||||
error:
|
||||
KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user