🔥 Remove kubeconfig and rely on service account
This commit is contained in:
parent
a42768ca49
commit
33f95bad5a
@ -51,7 +51,7 @@ public class LogResource
|
||||
|
||||
public List<KubernetesLog> getLogs(Pod pod, LocalDateTime from)
|
||||
{
|
||||
String command = String.format("kubectl --kubeconfig=%s logs %s -n %s --timestamps --tail=1000", clientProvider.pathToKubeconfig(), pod.getMetadata().getName(), pod.getMetadata().getNamespace());
|
||||
String command = String.format("kubectl logs %s -n %s --timestamps --tail=1000", pod.getMetadata().getName(), pod.getMetadata().getNamespace());
|
||||
List<KubernetesLog> result = new ArrayList<>();
|
||||
List<String> logs = processRunner.runToLines(command);
|
||||
for (String log : logs)
|
||||
|
||||
@ -44,7 +44,7 @@ public class PodResource
|
||||
private List<EnvVar> getVars(Pod pod)
|
||||
{
|
||||
List<EnvVar> result = new ArrayList<>();
|
||||
List<String> lines = processRunner.runToLines(String.format("kubectl --kubeconfig=%s exec -it %s -n %s -- env", clientProvider.pathToKubeconfig(), pod.getMetadata().getName(), pod.getMetadata().getNamespace()));
|
||||
List<String> lines = processRunner.runToLines(String.format("kubectl exec -it %s -n %s -- env", pod.getMetadata().getName(), pod.getMetadata().getNamespace()));
|
||||
for (String line : lines)
|
||||
{
|
||||
int indexOfFirstEquals = line.indexOf("=");
|
||||
|
||||
@ -8,19 +8,29 @@ import dev.dinauer.utils.ClientProvider;
|
||||
import io.fabric8.kubernetes.client.Watch;
|
||||
import io.fabric8.kubernetes.client.Watcher;
|
||||
import io.fabric8.kubernetes.client.WatcherException;
|
||||
import io.quarkus.security.UnauthorizedException;
|
||||
import io.smallrye.jwt.auth.principal.JWTParser;
|
||||
import io.smallrye.jwt.auth.principal.ParseException;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.websocket.CloseReason;
|
||||
import jakarta.websocket.OnClose;
|
||||
import jakarta.websocket.OnOpen;
|
||||
import jakarta.websocket.Session;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import org.eclipse.microprofile.context.ManagedExecutor;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@ServerEndpoint("/watch/{resource-type}/{namespace}")
|
||||
@ApplicationScoped
|
||||
@ -28,44 +38,58 @@ public class ResourceWebsocket
|
||||
{
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
@Inject
|
||||
Logger LOG;
|
||||
|
||||
@Inject
|
||||
ClientProvider clientProvider;
|
||||
|
||||
@Inject
|
||||
ManagedExecutor executor;
|
||||
|
||||
@Inject
|
||||
JWTParser parser;
|
||||
|
||||
private final Map<Session, Watch> sessions = new HashMap<>();
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("resource-type") String resourceType, @PathParam("namespace") String namespace)
|
||||
public void onOpen(Session session, @PathParam("resource-type") String resourceType, @PathParam("namespace") String namespace) throws ParseException
|
||||
{
|
||||
executor.runAsync(() ->
|
||||
JsonWebToken token = getToken(session.getQueryString());
|
||||
if (isValid(token))
|
||||
{
|
||||
if (ResourceType.POD.equals(resourceType))
|
||||
executor.runAsync(() ->
|
||||
{
|
||||
String version = clientProvider.getClient().pods().inAnyNamespace().list().getMetadata().getResourceVersion();
|
||||
if (isGlobal(namespace))
|
||||
if (ResourceType.POD.equals(resourceType))
|
||||
{
|
||||
send(session, EventType.INIT, clientProvider.getClient().pods().inAnyNamespace().list().getItems());
|
||||
sessions.put(session, clientProvider.getClient().pods().inAnyNamespace().withResourceVersion(version).watch(getWatcher(session)));
|
||||
String version = clientProvider.getClient().pods().inAnyNamespace().list().getMetadata().getResourceVersion();
|
||||
if (isGlobal(namespace))
|
||||
{
|
||||
send(session, EventType.INIT, clientProvider.getClient().pods().inAnyNamespace().list().getItems());
|
||||
sessions.put(session, clientProvider.getClient().pods().inAnyNamespace().withResourceVersion(version).watch(getWatcher(session)));
|
||||
}
|
||||
else
|
||||
{
|
||||
send(session, EventType.INIT, clientProvider.getClient().pods().inNamespace(namespace).list().getItems());
|
||||
sessions.put(session, clientProvider.getClient().pods().inNamespace(namespace).withResourceVersion(version).watch(getWatcher(session)));
|
||||
}
|
||||
}
|
||||
else
|
||||
if (ResourceType.CONFIG_MAP.equals(resourceType))
|
||||
{
|
||||
sessions.put(session, clientProvider.getClient().pods().inNamespace(namespace).watch(getWatcher(session)));
|
||||
String version = clientProvider.getClient().configMaps().inAnyNamespace().list().getMetadata().getResourceVersion();
|
||||
if (isGlobal(namespace))
|
||||
{
|
||||
send(session, EventType.INIT, clientProvider.getClient().configMaps().inAnyNamespace().list().getItems());
|
||||
sessions.put(session, clientProvider.getClient().configMaps().inAnyNamespace().withResourceVersion(version).watch(getWatcher(session)));
|
||||
}
|
||||
else
|
||||
{
|
||||
send(session, EventType.INIT, clientProvider.getClient().configMaps().inNamespace(namespace).list().getItems());
|
||||
sessions.put(session, clientProvider.getClient().configMaps().inNamespace(namespace).withResourceVersion(version).watch(getWatcher(session)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ResourceType.CONFIG_MAP.equals(resourceType))
|
||||
{
|
||||
if (isGlobal(namespace))
|
||||
{
|
||||
sessions.put(session, clientProvider.getClient().configMaps().inAnyNamespace().watch(getWatcher(session)));
|
||||
}
|
||||
else
|
||||
{
|
||||
sessions.put(session, clientProvider.getClient().configMaps().inNamespace(namespace).watch(getWatcher(session)));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@OnClose
|
||||
@ -76,6 +100,14 @@ public class ResourceWebsocket
|
||||
{
|
||||
watch.close();
|
||||
}
|
||||
try
|
||||
{
|
||||
session.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Watcher<T> getWatcher(Session session)
|
||||
@ -119,4 +151,31 @@ public class ResourceWebsocket
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private JsonWebToken getToken(String query) throws ParseException
|
||||
{
|
||||
for (String param : query.split("&"))
|
||||
{
|
||||
String[] sections = param.split("=", 2);
|
||||
if (sections.length == 2)
|
||||
{
|
||||
if (sections[0].equals("token"))
|
||||
{
|
||||
return parser.parse(sections[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG.error("Token cannot be null.");
|
||||
throw new RuntimeException("Token cannot be null.");
|
||||
}
|
||||
|
||||
private boolean isValid(JsonWebToken token)
|
||||
{
|
||||
Optional<String> purpose = token.claim("purpose");
|
||||
if (purpose.isPresent())
|
||||
{
|
||||
return purpose.get().equals("ws:connect");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
package dev.dinauer.inspect.websocket;
|
||||
|
||||
import com.arjuna.ats.internal.arjuna.Header;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import io.quarkus.security.UnauthorizedException;
|
||||
import io.smallrye.jwt.auth.principal.JWTParser;
|
||||
import io.smallrye.jwt.auth.principal.ParseException;
|
||||
import io.smallrye.jwt.build.Jwt;
|
||||
import io.smallrye.jwt.build.JwtClaimsBuilder;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Path("/websocket-session")
|
||||
public class WebsocketSessionResource
|
||||
{
|
||||
@Inject
|
||||
JsonWebToken token;
|
||||
|
||||
@POST
|
||||
@Authenticated
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String getSession()
|
||||
{
|
||||
if (token != null)
|
||||
{
|
||||
JwtClaimsBuilder builder = Jwt.upn(token.getName()).issuer(token.getIssuer()).expiresAt(ZonedDateTime.now().plusSeconds(15).toInstant());
|
||||
Optional<List<String>> namespaces = token.claim("namespaces");
|
||||
if (namespaces.isPresent())
|
||||
{
|
||||
builder = builder.claim("namespaces", namespaces.get());
|
||||
}
|
||||
Optional<List<String>> resources = token.claim("resources");
|
||||
if (resources.isPresent())
|
||||
{
|
||||
builder = builder.claim("resources", resources.get());
|
||||
}
|
||||
return builder.claim("purpose", "ws:connect").sign();
|
||||
}
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
package dev.dinauer.login;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.fabric8.kubernetes.api.model.ObjectMeta;
|
||||
import io.smallrye.jwt.build.Jwt;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
@ -10,12 +13,15 @@ import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Path("/login")
|
||||
@ApplicationScoped
|
||||
public class LoginResource
|
||||
{
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
@Inject
|
||||
Logger LOG;
|
||||
|
||||
@ -33,7 +39,11 @@ public class LoginResource
|
||||
UserEntity user = userOptional.get();
|
||||
if(BcryptUtil.matches(login.password(), user.getPassword()))
|
||||
{
|
||||
return Jwt.upn(user.getId()).expiresAt(ZonedDateTime.now().plusDays(15).toInstant()).groups(user.getRoles()).sign();
|
||||
return Jwt
|
||||
.upn(user.getId())
|
||||
.expiresAt(ZonedDateTime.now().plusDays(15).toInstant())
|
||||
.groups(user.getRoles())
|
||||
.sign();
|
||||
}
|
||||
LOG.info("Cannot access user. Forbidden");
|
||||
throw new ForbiddenException();
|
||||
|
||||
@ -59,7 +59,7 @@ public class TopNodesService
|
||||
|
||||
private List<String> runTopNodesCommand()
|
||||
{
|
||||
String command = String.format("kubectl --kubeconfig=%s top nodes --no-headers", clientProvider.pathToKubeconfig());
|
||||
String command = String.format("kubectl top nodes --no-headers");
|
||||
return processRunner.runToLines(command);
|
||||
}
|
||||
|
||||
|
||||
@ -14,19 +14,11 @@ import java.io.File;
|
||||
@ApplicationScoped
|
||||
public class ClientProvider
|
||||
{
|
||||
@ConfigProperty(name = "dev.dinauer.kobooboo.kubeconfigs.dir")
|
||||
String configFilePath;
|
||||
|
||||
@Inject
|
||||
Vertx vertx;
|
||||
|
||||
public KubernetesClient getClient()
|
||||
{
|
||||
return new KubernetesClientBuilder().withConfig(Config.fromKubeconfig(new File(configFilePath))).withHttpClientFactory(new VertxHttpClientFactory(vertx.getDelegate())).build();
|
||||
}
|
||||
|
||||
public String pathToKubeconfig()
|
||||
{
|
||||
return configFilePath;
|
||||
return new KubernetesClientBuilder().withHttpClientFactory(new VertxHttpClientFactory(vertx.getDelegate())).build();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,13 +3,9 @@ quarkus.http.root-path=/api
|
||||
%dev.quarkus.http.cors.origins=/.*/
|
||||
%dev.quarkus.http.port=9090
|
||||
|
||||
%dev,test.dev.dinauer.kobooboo.kubeconfigs.dir=/var/lib/kubooboo/config
|
||||
dev.dinauer.kubooboo.work.dir=/var/lib/kubooboo/work
|
||||
%dev.dev.dinauer.kobooboo.kubeconfigs.dir=/home/andreas/.kube/config
|
||||
%dev.dev.dinauer.kubooboo.work.dir=/home/andreas/Documents/dev/kubooboo/backend/src/main/resources/dev
|
||||
|
||||
%prod.dev.dinauer.kobooboo.kubeconfigs.dir=${KUBECONFIG_LOCATION}
|
||||
|
||||
# Keys
|
||||
%prod.smallrye.jwt.sign.key.location=${PRIVATE_KEY_LOCATION}
|
||||
%prod.mp.jwt.verify.publickey.location=${PUBLIC_KEY_LOCATION}
|
||||
@ -18,7 +14,7 @@ dev.dinauer.kubooboo.work.dir=/var/lib/kubooboo/work
|
||||
%dev.mp.jwt.verify.publickey.location=publicKey.pem
|
||||
|
||||
# Postgres
|
||||
%dev.quarkus.datasource.db-kind = postgresql
|
||||
quarkus.datasource.db-kind = postgresql
|
||||
%dev.quarkus.datasource.username = postgres
|
||||
%dev.quarkus.datasource.password = postgres
|
||||
%dev.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:6666/postgres
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user