Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ARTEMIS-5219] load all certificates
Browse files Browse the repository at this point in the history
In the JAAS login module for Kubernetes, the current implementation is
loading only a single CA from the bundle that is given by OpenShift,
which in turn leads to an issue where the broker don't find a proper
PKIX path.

This is fixing the issue by loading every certificates from the bundle.

The commit also brings in a new command line that can be used to
test if pod is well configured to perform token reviews or not.
lavocatt committed Dec 17, 2024
1 parent 7eaea5a commit b045ef2
Showing 5 changed files with 161 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@
import org.apache.activemq.artemis.cli.commands.PrintVersion;
import org.apache.activemq.artemis.cli.commands.Run;
import org.apache.activemq.artemis.cli.commands.Stop;
import org.apache.activemq.artemis.cli.commands.TokenReview;
import org.apache.activemq.artemis.cli.commands.Upgrade;
import org.apache.activemq.artemis.cli.commands.activation.ActivationGroup;
import org.apache.activemq.artemis.cli.commands.address.AddressGroup;
@@ -53,6 +54,7 @@
import org.apache.activemq.artemis.cli.commands.messages.perf.PerfGroup;
import org.apache.activemq.artemis.cli.commands.queue.QueueGroup;
import org.apache.activemq.artemis.cli.commands.tools.DataGroup;
import org.apache.activemq.artemis.cli.commands.tools.Kubernetes;
import org.apache.activemq.artemis.cli.commands.tools.journal.PerfJournal;
import org.apache.activemq.artemis.cli.commands.user.UserGroup;
import org.apache.activemq.artemis.dto.ManagementContextDTO;
@@ -308,6 +310,8 @@ public static CommandLine buildCommand(boolean includeInstanceCommands, boolean

commandLine.addSubcommand(new Completion());

commandLine.addSubcommand(new Kubernetes(commandLine));

return commandLine;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.activemq.artemis.cli.commands;

import org.apache.activemq.artemis.spi.core.security.jaas.kubernetes.client.KubernetesClient;
import org.apache.activemq.artemis.spi.core.security.jaas.kubernetes.client.KubernetesClientImpl;
import picocli.CommandLine;

@CommandLine.Command(name = "token-review", description = "Perform a kubernetes token review")
public class TokenReview extends InputAbstract {

@CommandLine.Option(names = "--kube-host", description = "hostname for kubernetes api server")
protected String kubeHost;

@CommandLine.Option(names = "--kube-port", description = "port for kubernetes api server")
protected String kubeport;

@CommandLine.Option(names = "--token-path", description = "path to the token to access the kubernetes api server with access to token reviews")
protected String tokenpath;

@CommandLine.Option(names = "--ca-path", description = "path to the kubernetes api server trusted CAs")
protected String capath;

@CommandLine.Option(names = "--token", description = "token to review")
protected String token;

@Override
public Object execute(ActionContext context) throws Exception {
super.execute(context);

context.out.println();
if (kubeHost == null) {
kubeHost = input("--kube-host", "What is the cluster host?", System.getProperty(KubernetesClientImpl.KUBERNETES_HOST));
}
if (kubeport == null) {
kubeport = input("--kube-port", "What is the cluster port?", System.getProperty(KubernetesClientImpl.KUBERNETES_PORT));
}
if (tokenpath == null) {
tokenpath = input("--token-path", "What is the token path?", System.getProperty(KubernetesClientImpl.KUBERNETES_TOKEN_PATH));
}
if (capath == null) {
capath = input("--ca-path", "What is the ca path?", System.getProperty(KubernetesClientImpl.KUBERNETES_CA_PATH));
}
if (token == null) {
token = input("--token", "What is the token?", "");
}
String prevKSH = System.getProperty("KUBERNETES_SERVICE_HOST", kubeHost);
String prevKSP = System.getProperty("KUBERNETES_SERVICE_PORT", kubeport);
String prevTP = System.getProperty("KUBERNETES_TOKEN_PATH", tokenpath);
String prevCP = System.getProperty("KUBERNETES_CA_PATH", capath);
System.setProperty("KUBERNETES_SERVICE_HOST", kubeHost);
System.setProperty("KUBERNETES_SERVICE_PORT", kubeport);
System.setProperty("KUBERNETES_TOKEN_PATH", tokenpath);
System.setProperty("KUBERNETES_CA_PATH", capath);
try {
KubernetesClient client = new KubernetesClientImpl();
org.apache.activemq.artemis.spi.core.security.jaas.kubernetes.model.TokenReview tr = client.getTokenReview(token);
context.out.println("*******************************************************************************************************************************");
context.out.println("* Performing a token review on:");
context.out.println("* host:: " + kubeHost);
context.out.println("* port:: " + kubeport);
context.out.println("* tokenpath:: " + tokenpath);
context.out.println("* capath:: " + capath);
context.out.println("*");
context.out.println("* Result:");
context.out.println("* token:: " + token);
context.out.println("* username:: " + tr.getUsername());
context.out.println("*******************************************************************************************************************************");
context.out.println();
} finally {
System.setProperty("KUBERNETES_SERVICE_HOST", prevKSH);
System.setProperty("KUBERNETES_SERVICE_PORT", prevKSP);
System.setProperty("KUBERNETES_TOKEN_PATH", prevTP);
System.setProperty("KUBERNETES_CA_PATH", prevCP);
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.activemq.artemis.cli.commands.tools;

import org.apache.activemq.artemis.cli.commands.HelpAction;
import org.apache.activemq.artemis.cli.commands.TokenReview;
import org.apache.activemq.artemis.cli.commands.tools.journal.CompactJournal;
import org.apache.activemq.artemis.cli.commands.tools.journal.DecodeJournal;
import org.apache.activemq.artemis.cli.commands.tools.journal.EncodeJournal;
import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataExporter;
import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataImporter;
import picocli.CommandLine;
import picocli.CommandLine.Command;

@Command(name = "kube", description = "use 'help kube' for sub commands list", subcommands = {TokenReview.class})
public class Kubernetes implements Runnable {

CommandLine commandLine;

public Kubernetes(CommandLine commandLine) {
this.commandLine = commandLine;
}

@Override
public void run() {
HelpAction.help(commandLine, "data");
}

}
Original file line number Diff line number Diff line change
@@ -328,10 +328,10 @@ private Collection<? extends CRL> loadCRL() throws Exception {
}
}

private static KeyStore loadKeystore(final String keystoreProvider,
final String keystoreType,
final String keystorePath,
final String keystorePassword) throws Exception {
public static KeyStore loadKeystore(final String keystoreProvider,
final String keystoreType,
final String keystorePath,
final String keystorePassword) throws Exception {
checkPemProviderLoaded(keystoreType);
KeyStore ks = keystoreProvider == null ? KeyStore.getInstance(keystoreType) : KeyStore.getInstance(keystoreType, keystoreProvider);
InputStream in = null;
Original file line number Diff line number Diff line change
@@ -30,13 +30,17 @@
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.Scanner;
import java.util.UUID;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import org.apache.activemq.artemis.core.remoting.impl.ssl.SSLSupport;
import org.apache.activemq.artemis.spi.core.security.jaas.kubernetes.model.TokenReview;
import org.apache.activemq.artemis.utils.JsonLoader;
import org.slf4j.Logger;
@@ -46,10 +50,10 @@ public class KubernetesClientImpl implements KubernetesClient {

private static final Logger logger = LoggerFactory.getLogger(KubernetesClientImpl.class);

private static final String KUBERNETES_HOST = "KUBERNETES_SERVICE_HOST";
private static final String KUBERNETES_PORT = "KUBERNETES_SERVICE_PORT";
private static final String KUBERNETES_TOKEN_PATH = "KUBERNETES_TOKEN_PATH";
private static final String KUBERNETES_CA_PATH = "KUBERNETES_CA_PATH";
public static final String KUBERNETES_HOST = "KUBERNETES_SERVICE_HOST";
public static final String KUBERNETES_PORT = "KUBERNETES_SERVICE_PORT";
public static final String KUBERNETES_TOKEN_PATH = "KUBERNETES_TOKEN_PATH";
public static final String KUBERNETES_CA_PATH = "KUBERNETES_CA_PATH";

private static final String KUBERNETES_TOKENREVIEW_URI_PATTERN = "https://%s:%s/apis/authentication.k8s.io/v1/tokenreviews";

@@ -157,18 +161,12 @@ private SSLContext buildSSLContext() throws Exception {
logger.debug("Kubernetes CA certificate not found at: {}. Truststore not configured", caPath);
return ctx;
}
try (InputStream fis = new FileInputStream(certFile)) {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(fis);
trustStore.load(null, null);
trustStore.setCertificateEntry(certFile.getName(), certificate);
TrustManagerFactory tmFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmFactory.init(trustStore);

ctx.init(null, tmFactory.getTrustManagers(), new SecureRandom());
}
KeyStore trustStore = SSLSupport.loadKeystore(null, "PEMCA", caPath, null);
TrustManagerFactory tmFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmFactory.init(trustStore);

ctx.init(null, tmFactory.getTrustManagers(), new SecureRandom());
return ctx;
}
}

0 comments on commit b045ef2

Please sign in to comment.