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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import io.github.inseefrlab.helmwrapper.configuration.HelmConfiguration;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
Expand Down Expand Up @@ -30,7 +32,9 @@
new ProcessListener() {
@Override
public void afterStart(Process process, ProcessExecutor executor) {
process.info().commandLine().ifPresent(cli -> LOGGER.info(cli));
process.info()
.commandLine()
.ifPresent(cli -> LOGGER.info(redactToken(cli)));
super.afterStart(process, executor);
}
});
Expand Down Expand Up @@ -114,6 +118,29 @@
if (helmConfiguration.getAsKubeUser() != null) {
newCommand =
newCommand.concat(" --kube-as-user " + helmConfiguration.getAsKubeUser() + " ");

// Temp fix for https://github.com/kubernetes/kubernetes/issues/93474
try {
String token =
Files.readString(
Path.of(
"/var/run/secrets/kubernetes.io/serviceaccount/token"))
.trim();
String apiserver =
"https://"
+ System.getenv("KUBERNETES_SERVICE_HOST")
+ ":"
+ System.getenv("KUBERNETES_SERVICE_PORT");
newCommand =
newCommand.concat(
" --kube-ca-file=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt --kube-token="
+ token
+ " --kube-apiserver="
+ apiserver
+ " ");
} catch (IOException e) {

Check warning on line 141 in helm-wrapper/src/main/java/io/github/inseefrlab/helmwrapper/utils/Command.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=InseeFrLab_onyxia-api&issues=AaFpHZ79OvglcMGQI2hZ&open=AaFpHZ79OvglcMGQI2hZ&pullRequest=693
LOGGER.debug("Could not retrieve in-cluster token");
}
}
String kubeConfig = null;
if (StringUtils.isNotEmpty(helmConfiguration.getApiserverUrl())) {
Expand Down Expand Up @@ -146,6 +173,10 @@
return newCommand;
}

private static String redactToken(String commandLine) {
return commandLine.replaceAll("--kube-token[= ]+[^\\s]+", "--kube-token=[REDACTED]");
}

public static void safeConcat(StringBuilder currentCommand, String toConcat)
throws IllegalArgumentException {
if (!safeToConcatenate.matcher(toConcat).matches()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.github.inseefrlab.helmwrapper.utils;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;

public class RedactTokenTest {

Check warning on line 8 in helm-wrapper/src/test/java/io/github/inseefrlab/helmwrapper/utils/RedactTokenTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove redundant visibility modifiers from the methods of this test class.

See more on https://sonarcloud.io/project/issues?id=InseeFrLab_onyxia-api&issues=AaFpQBPU2AsIuq_znXzR&open=AaFpQBPU2AsIuq_znXzR&pullRequest=693

private String invokeRedactToken(String commandLine) throws Exception {
Method method = Command.class.getDeclaredMethod("redactToken", String.class);
method.setAccessible(true);
return (String) method.invoke(null, commandLine);
}

@Test
public void shouldRedactKubeTokenWithEquals() throws Exception {

Check warning on line 17 in helm-wrapper/src/test/java/io/github/inseefrlab/helmwrapper/utils/RedactTokenTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace these 6 tests with a single Parameterized one.

See more on https://sonarcloud.io/project/issues?id=InseeFrLab_onyxia-api&issues=AaFpQBPU2AsIuq_znXzS&open=AaFpQBPU2AsIuq_znXzS&pullRequest=693
String input =
"helm ls --kube-token=eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0In0.test";
String result = invokeRedactToken(input);
assertEquals("helm ls --kube-token=[REDACTED]", result);
}

@Test
public void shouldRedactKubeTokenWithSpace() throws Exception {
String input =
"helm ls --kube-token eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0In0.test";
String result = invokeRedactToken(input);
assertEquals("helm ls --kube-token=[REDACTED]", result);
}

@Test
public void shouldRedactMultipleKubeTokens() throws Exception {
String input = "helm ls --kube-token=token1 --kube-token=token2";
String result = invokeRedactToken(input);
assertEquals("helm ls --kube-token=[REDACTED] --kube-token=[REDACTED]", result);
}

@Test
public void shouldLeaveOtherOptionsUnchanged() throws Exception {
String input =
"helm ls --kubeconfig=/path/to/config --kube-apiserver=https://api.example.com";
String result = invokeRedactToken(input);
assertEquals(
"helm ls --kubeconfig=/path/to/config --kube-apiserver=https://api.example.com",
result);
}

@Test
public void shouldNotRedactIfNoToken() throws Exception {
String input = "helm ls --namespace default";
String result = invokeRedactToken(input);
assertEquals("helm ls --namespace default", result);
}

@Test
public void shouldRedactTokenWithTrailingOptions() throws Exception {
String input = "helm ls --kube-token=secret123 --namespace myns --output json";
String result = invokeRedactToken(input);
assertEquals("helm ls --kube-token=[REDACTED] --namespace myns --output json", result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public HelmVersionService defaultHelmVersionService() {
return new HelmVersionService();
}

public HelmConfiguration getConfiguration(Region region, User user) {
public HelmConfiguration getConfiguration(Region region, User user, String namespace) {
final HelmConfiguration helmConfiguration = new HelmConfiguration();
if (region.getServices().getServer() != null) {
final Region.Auth auth = region.getServices().getServer().getAuth();
Expand All @@ -47,6 +47,12 @@ public HelmConfiguration getConfiguration(Region region, User user) {
helmConfiguration.setAsKubeUser(username);
}

if (region.getServices().getAuthenticationMode()
== Region.Services.AuthenticationMode.SERVICEACCOUNTINTARGETNAMESPACE) {
helmConfiguration.setAsKubeUser(
String.format("system:serviceaccount:%s:onyxia-local", namespace));
}

if (region.getServices().getAuthenticationMode()
== Region.Services.AuthenticationMode.TOKEN_PASSTHROUGH) {
helmConfiguration.setKubeToken((String) user.getAttributes().get("access_token"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
// In case of SERVICEACCOUNT authentication, we can safely mutualize and use a single
// KubernetesClient
if (region.getServices().getAuthenticationMode()
== Region.Services.AuthenticationMode.SERVICEACCOUNT) {
== Region.Services.AuthenticationMode.SERVICEACCOUNT
|| region.getServices().getAuthenticationMode()
== Region.Services.AuthenticationMode.SERVICEACCOUNTINTARGETNAMESPACE) {
if (userKubernetesClientCache != null) {
return userKubernetesClientCache;
}
Expand All @@ -60,7 +62,7 @@
if (region.getServices().getAuthenticationMode()
== Region.Services.AuthenticationMode.IMPERSONATE) {
config.setImpersonateUsername(username);
config.setImpersonateGroups(null);

Check warning on line 65 in onyxia-api/src/main/java/fr/insee/onyxia/api/configuration/kubernetes/KubernetesClientProvider.java

View workflow job for this annotation

GitHub Actions / build

non-varargs call of varargs method with inexact argument type for last parameter;

Check warning on line 65 in onyxia-api/src/main/java/fr/insee/onyxia/api/configuration/kubernetes/KubernetesClientProvider.java

View workflow job for this annotation

GitHub Actions / build

non-varargs call of varargs method with inexact argument type for last parameter;
}

if (region.getServices().getAuthenticationMode()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ public HelmAppsService(
this.helmReleaseHealthResolver = helmReleaseHealthResolver;
}

private HelmConfiguration getHelmConfiguration(Region region, User user) {
return helmClientProvider.getConfiguration(region, user);
private HelmConfiguration getHelmConfiguration(Region region, User user, String namespace) {
return helmClientProvider.getConfiguration(region, user, namespace);
}

private HelmInstallService getHelmInstallService() {
Expand Down Expand Up @@ -118,7 +118,7 @@ public Collection<Object> installApp(
HelmInstaller res =
getHelmInstallService()
.installChart(
getHelmConfiguration(region, user),
getHelmConfiguration(region, user, namespaceId),
catalogId + "/" + pkg.getName(),
namespaceId,
requestDTO.getName(),
Expand Down Expand Up @@ -180,7 +180,8 @@ public CompletableFuture<ServicesListing> getUserServices(
Arrays.asList(
getHelmInstallService()
.listChartInstall(
getHelmConfiguration(region, user),
getHelmConfiguration(
region, user, project.getNamespace()),
project.getNamespace()));
} catch (Exception e) {
return CompletableFuture.completedFuture(new ServicesListing());
Expand Down Expand Up @@ -234,13 +235,14 @@ public Service getUserService(Region region, Project project, User user, String
if (serviceId.startsWith("/")) {
serviceId = serviceId.substring(1);
}
String namespace =
kubernetesService.determineNamespaceAndCreateIfNeeded(region, project, user);
HelmLs result =
getHelmInstallService()
.getAppById(
getHelmConfiguration(region, user),
getHelmConfiguration(region, user, namespace),
serviceId,
kubernetesService.determineNamespaceAndCreateIfNeeded(
region, project, user));
namespace);
return getHelmApp(region, user, result);
}

Expand All @@ -257,14 +259,15 @@ public UninstallService destroyService(
// If bulk in kub we ignore the path and delete every helm release
HelmLs[] releases =
getHelmInstallService()
.listChartInstall(getHelmConfiguration(region, user), namespace);
.listChartInstall(
getHelmConfiguration(region, user, namespace), namespace);
for (HelmLs release : releases) {
status =
Math.max(
0,
getHelmInstallService()
.uninstaller(
getHelmConfiguration(region, user),
getHelmConfiguration(region, user, namespace),
release.getName(),
namespace));
UninstallServiceEvent uninstallServiceEvent =
Expand All @@ -277,7 +280,9 @@ public UninstallService destroyService(
status =
getHelmInstallService()
.uninstaller(
getHelmConfiguration(region, user), cannonicalPath, namespace);
getHelmConfiguration(region, user, namespace),
cannonicalPath,
namespace);
UninstallServiceEvent uninstallServiceEvent =
new UninstallServiceEvent(namespace, cannonicalPath, user.getIdep());
onyxiaEventPublisher.publishEvent(uninstallServiceEvent);
Expand All @@ -290,7 +295,7 @@ private Service getHelmApp(Region region, User user, HelmLs release) {
HelmReleaseInfo helmReleaseInfo =
getHelmInstallService()
.getAll(
getHelmConfiguration(region, user),
getHelmConfiguration(region, user, release.getNamespace()),
release.getName(),
release.getNamespace());
Service service =
Expand Down Expand Up @@ -481,7 +486,7 @@ public void suspendOrResume(
if (suspend) {
getHelmInstallService()
.suspend(
getHelmConfiguration(region, user),
getHelmConfiguration(region, user, namespaceId),
catalogId + "/" + chartName,
namespaceId,
serviceId,
Expand All @@ -493,7 +498,7 @@ public void suspendOrResume(
} else {
getHelmInstallService()
.resume(
getHelmConfiguration(region, user),
getHelmConfiguration(region, user, namespaceId),
catalogId + "/" + chartName,
namespaceId,
serviceId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ public static enum AuthenticationMode {
@JsonProperty("serviceAccount")
@JsonAlias("admin")
SERVICEACCOUNT,

@JsonProperty("serviceAccountInTargetNamespace")
SERVICEACCOUNTINTARGETNAMESPACE,
@JsonProperty("tokenPassthrough")
TOKEN_PASSTHROUGH
}
Expand Down
Loading