Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 7.9-SNAPSHOT

#### Bugs
* Fix #8022: (kubernetes-client-api) Client key algorithm is now detected from key material returned by kubeconfig exec credential plugins. Previously only static `client-key-data` / `client-key` entries ran `getKeyAlgorithm` (see #3535); exec-provided ECDSA keys left `clientKeyAlgo` unset so `CertUtils` defaulted to RSA and failed with `Invalid DER: object is not integer`
* Fix #7807: (kube-api-test) The `KUBE_API_TEST_STARTUP_TIMEOUT` environment variable is now parsed instead of throwing `ClassCastException` on every use — the value (always a `String`) was passed to `Class.cast()` for an `Integer`/`Boolean` target, which only widens reference types and never parses, so the timeout could not be configured via environment at all. A new `startupTimeout` attribute on `@EnableKubeAPIServer` (e.g. `@EnableKubeAPIServer(startupTimeout = 180000)`) allows overriding it declaratively, and all three configuration paths (annotation, builder, env var) now reject non-positive values
* Fix #7983: (mockwebserver) WebSocket upgrades are now performed synchronously from the Vert.x request handler instead of from the asynchronous `HttpServerRequest#body()` callback. Deferring the upgrade let the request end event be processed first, so `HttpServerRequest#toWebSocket()` intermittently threw `IllegalStateException: Request has already been read` and the upgrade was lost (surfacing as flaky `exec`/`attach` mock-server tests). Upgrade requests carry no body, so they are detected via the `Upgrade` header and upgraded before the request is read; the asynchronous path is unchanged for regular HTTP requests
* Fix #7955: (java-generator) Malicious CRD schema values can no longer inject executable code into the generated Java sources. Schema-controlled values (enum values, CRD group/version/names, property names, descriptions and defaults) are emitted as fully escaped Java string literals, so a value carrying a Unicode-escaped quote cannot break out of its literal once `javac` decodes it. As a defense in depth, each generated class is also re-parsed and structurally validated before it is written (with Java Unicode escape preprocessing enabled to match `javac`), aborting generation on any residual structural mismatch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ private static void mergeKubeConfigExecCredential(io.fabric8.kubernetes.client.C
&& Utils.isNotNullOrEmpty(ec.status.clientKeyData)) {
config.setClientCertData(ec.status.clientCertificateData);
config.setClientKeyData(ec.status.clientKeyData);
// Detect key algorithm from exec-provided material (same as static kubeconfig keys).
// Without this, clientKeyAlgo stays unset and CertUtils defaults to RSA, which fails for ECDSA.
config.setClientKeyAlgo(getKeyAlgorithm(null, config.getClientKeyData()));
} else {
logger.warn("No token or certificate returned");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ class ConfigTest {
.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec-cert-auth-ec-invalid"));
private static final String TEST_KUBECONFIG_EXEC_WIN_FILE_CERT_AUTH_EC_INVALID = Utils
.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec-win-cert-auth-ec-invalid"));
private static final String TEST_KUBECONFIG_EXEC_FILE_CERT_AUTH_EC = Utils
.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec-cert-auth-ec"));
private static final String TEST_KUBECONFIG_EXEC_WIN_FILE_CERT_AUTH_EC = Utils
.filePath(ConfigTest.class.getResource("/test-kubeconfig-exec-win-cert-auth-ec"));
private static final String TEST_CERT_GENERATOR_FILE = Utils.filePath(ConfigTest.class.getResource("/cert-generator"));
private static final String TEST_CERT_GENERATOR_EC_FILE = Utils
.filePath(ConfigTest.class.getResource("/cert-generator-ec"));
private static final String TEST_KUBECONFIG_TLS_SERVER_NAME_FILE = Utils
.filePath(ConfigTest.class.getResource("/test-kubeconfig-tls-server-name"));

Expand Down Expand Up @@ -797,6 +803,27 @@ void autoConfigure_whenInvalidArgsProvided_thenDoNotLoadClientCertData() throws
.hasFieldOrPropertyWithValue("clientKeyData", null);
}

@Test
@DisplayName("when exec returns ECDSA client key, then identify client key algorithm as EC (#8022)")
void autoConfigure_whenExecReturnsEcdsaClientKey_thenSetClientKeyAlgoToEc() throws Exception {
// Given
if (FileSystem.getCurrent() == FileSystem.WINDOWS) {
System.setProperty("kubeconfig", TEST_KUBECONFIG_EXEC_WIN_FILE_CERT_AUTH_EC);
} else {
Files.setPosixFilePermissions(Paths.get(TEST_CERT_GENERATOR_EC_FILE), PosixFilePermissions.fromString("rwxrwxr-x"));
System.setProperty("kubeconfig", TEST_KUBECONFIG_EXEC_FILE_CERT_AUTH_EC);
}
// When
Config config = Config.autoConfigure(null);
// Then
assertThat(config)
.isNotNull()
.hasFieldOrPropertyWithValue("clientKeyAlgo", "EC")
.extracting(Config::getClientKeyData, InstanceOfAssertFactories.STRING)
.contains("BEGIN EC PRIVATE KEY");
assertThat(config.getClientCertData()).contains("BEGIN CERTIFICATE");
}

@Test
void should_accept_client_authentication_commands_args_with_spaces() throws Exception {
// Given
Expand Down
14 changes: 14 additions & 0 deletions kubernetes-client-api/src/test/resources/cert-generator-ec
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# Fixed sample ECDSA client certificate/key for unit tests.
# clientKeyData is a SEC1 EC private key PEM so algorithm detection yields "EC".
cat <<'EOF'
{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1",
"spec": {},
"status": {
"clientCertificateData": "-----BEGIN CERTIFICATE-----\nMIICUDCCATigAwIBAgIRAP5Y0VEn43LepIIhGBgZCm4wDQYJKoZIhvcNAQELBQAw\nFTETMBEGA1UEAxMKbWluaWt1YmVDQTAeFw0yMTExMDEwOTIyMThaFw0yMjExMDEw\nOTIyMThaMCUxEzARBgNVBAoTCmRldmVsb3BlcnMxDjAMBgNVBAMTBXV6YWlyMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkAMD3WlBNsN0tiNCZSC8gK/5O4rxmkgj\n953kvWgv11/AorgxDFZpr2+azYJAN6ZNt8hXoBlsrBtDDzKnfPJszqNWMFQwDgYD\nVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw\nHwYDVR0jBBgwFoAU4qycWT5q7c/4OgWIHdKILxBhjS8wDQYJKoZIhvcNAQELBQAD\nggEBANTomS4AL4z0cxd0KZQhts2ADlbgJQUmI7TU8+I70qoQhdvFC3xFuQM3KHVQ\nkt4amj+ahz74ZMGB8FH7SHvdLdb9yIsZQJttw6MVyrC3/GavRBQM3KwAB0CVMEQf\nRL/PYTXJSmPiicQH9BLUW9RzJwP4gnIlEj2yjLnAPyilH4LSiBvjE93nPBHdY0SF\n/v1/Jy3pnpcih05eHahFtMrE3FH1YaVCL2ncUGif//x9TNhR7WX2w0+X+fqY4w0Q\n4xmJCGW/DvYqBGqavdEYU1FMXvlhqQkRueYOGbU8P2VlhU5qd0Wdfdg8FokkdozL\nRUgkCbcwxOTea1Lit2iuDGzJezI=\n-----END CERTIFICATE-----\n",
"clientKeyData": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIICkeBChzRrBMifb5r9rzdq4FphWRzPz1eDmRTnqBugJoAoGCCqGSM49\nAwEHoUQDQgAEkAMD3WlBNsN0tiNCZSC8gK/5O4rxmkgj953kvWgv11/AorgxDFZp\nr2+azYJAN6ZNt8hXoBlsrBtDDzKnfPJszg==\n-----END EC PRIVATE KEY-----\n"
}
}
EOF
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@REM Fixed sample ECDSA client certificate/key for unit tests.
@echo off
echo {
echo "kind": "ExecCredential",
echo "apiVersion": "client.authentication.k8s.io/v1",
echo "spec": {},
echo "status": {
echo "clientCertificateData": "-----BEGIN CERTIFICATE-----\nMIICUDCCATigAwIBAgIRAP5Y0VEn43LepIIhGBgZCm4wDQYJKoZIhvcNAQELBQAw\nFTETMBEGA1UEAxMKbWluaWt1YmVDQTAeFw0yMTExMDEwOTIyMThaFw0yMjExMDEw\nOTIyMThaMCUxEzARBgNVBAoTCmRldmVsb3BlcnMxDjAMBgNVBAMTBXV6YWlyMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkAMD3WlBNsN0tiNCZSC8gK/5O4rxmkgj\n953kvWgv11/AorgxDFZpr2+azYJAN6ZNt8hXoBlsrBtDDzKnfPJszqNWMFQwDgYD\nVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw\nHwYDVR0jBBgwFoAU4qycWT5q7c/4OgWIHdKILxBhjS8wDQYJKoZIhvcNAQELBQAD\nggEBANTomS4AL4z0cxd0KZQhts2ADlbgJQUmI7TU8+I70qoQhdvFC3xFuQM3KHVQ\nkt4amj+ahz74ZMGB8FH7SHvdLdb9yIsZQJttw6MVyrC3/GavRBQM3KwAB0CVMEQf\nRL/PYTXJSmPiicQH9BLUW9RzJwP4gnIlEj2yjLnAPyilH4LSiBvjE93nPBHdY0SF\n/v1/Jy3pnpcih05eHahFtMrE3FH1YaVCL2ncUGif//x9TNhR7WX2w0+X+fqY4w0Q\n4xmJCGW/DvYqBGqavdEYU1FMXvlhqQkRueYOGbU8P2VlhU5qd0Wdfdg8FokkdozL\nRUgkCbcwxOTea1Lit2iuDGzJezI=\n-----END CERTIFICATE-----\n",
echo "clientKeyData": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIICkeBChzRrBMifb5r9rzdq4FphWRzPz1eDmRTnqBugJoAoGCCqGSM49\nAwEHoUQDQgAEkAMD3WlBNsN0tiNCZSC8gK/5O4rxmkgj953kvWgv11/AorgxDFZp\nr2+azYJAN6ZNt8hXoBlsrBtDDzKnfPJszg==\n-----END EC PRIVATE KEY-----\n"
echo }
echo }
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Config
clusters:
- cluster:
server: https://wherever
name: test
contexts:
- context:
cluster: test
user: test
name: test
current-context: test
users:
- name: test
user:
exec:
apiVersion: client.authentication.k8s.io/v1
args: []
command: ./cert-generator-ec
env: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Config
clusters:
- cluster:
server: https://wherever
name: test
contexts:
- context:
cluster: test
user: test
name: test
current-context: test
users:
- name: test
user:
exec:
apiVersion: client.authentication.k8s.io/v1
args: []
command: ".\cert-generator-ec-win.bat"
env: []