This is an automated email from the ASF dual-hosted git repository.
tdiesler pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new edb78e8fafd CAMEL-21899: camel-oauth - openshift does not maintain
http session
edb78e8fafd is described below
commit edb78e8fafd67d62c5877cab1053c67cd40da92c
Author: Thomas Diesler <[email protected]>
AuthorDate: Tue Apr 15 12:42:31 2025 +0200
CAMEL-21899: camel-oauth - openshift does not maintain http session
---
components/camel-oauth/helm/README.md | 21 ++++++++++++
components/camel-oauth/helm/etc/camel-realm.json | 5 +--
.../apache/camel/oauth/AbstractOAuthProcessor.java | 9 -----
.../apache/camel/oauth/InMemorySessionStore.java | 14 ++++++++
.../apache/camel/oauth/OAuthCodeFlowCallback.java | 3 +-
.../apache/camel/oauth/OAuthCodeFlowProcessor.java | 32 +++++++++++++++---
.../apache/camel/oauth/OAuthLogoutProcessor.java | 7 ++--
.../apache/camel/test/oauth/SSLCertTrustTest.java | 38 +++++++++++++++++++++-
.../modules/ROOT/pages/camel-jbang-kubernetes.adoc | 7 +---
.../commands/kubernetes/traits/IngressTrait.java | 9 +++--
10 files changed, 115 insertions(+), 30 deletions(-)
diff --git a/components/camel-oauth/helm/README.md
b/components/camel-oauth/helm/README.md
index 6563035d3c2..c7f7bbedad5 100644
--- a/components/camel-oauth/helm/README.md
+++ b/components/camel-oauth/helm/README.md
@@ -214,3 +214,24 @@ Verify access to the OIDC configuration
```
curl -s
https://keycloak.${OPENSHIFT_HOSTNAME}/realms/camel/.well-known/openid-configuration
| jq .
```
+
+### Modify Keycloak OIDC Config for OpenShift
+
+```sh
+kcadm config credentials --server https://keycloak.${OPENSHIFT_HOSTNAME}
--realm master --user admin --password admin
+
+# Show client config
+kcadm get clients -r camel | jq '.[] | select(.clientId=="camel-client")'
+
+CLIENT_ID=$(kcadm get clients -r camel --fields id,clientId | jq -r '.[] |
select(.clientId=="camel-client").id')
+
+# Update redirect URIs
+kcadm update clients/$CLIENT_ID -r camel -s
'redirectUris=["https://webapp.'${OPENSHIFT_HOSTNAME}'/auth"]'
+
+# Update post-logout redirect URIs
+kcadm update clients/$CLIENT_ID -r camel -s
'attributes."post.logout.redirect.uris"="https://webapp.'${OPENSHIFT_HOSTNAME}'/"'
+
+kcadm get realms | jq '.[] | select(.realm=="camel")'
+
+kcadm get keys -r camel
+```
\ No newline at end of file
diff --git a/components/camel-oauth/helm/etc/camel-realm.json
b/components/camel-oauth/helm/etc/camel-realm.json
index ef300a59d29..281d6960253 100644
--- a/components/camel-oauth/helm/etc/camel-realm.json
+++ b/components/camel-oauth/helm/etc/camel-realm.json
@@ -168,10 +168,11 @@
"consentRequired" : false,
"fullScopeAllowed" : false,
"redirectUris": [
- "http://127.0.0.1:8080/auth"
+ "https://example.local/auth",
+ "https://example.k3s/auth"
],
"attributes": {
- "post.logout.redirect.uris": "http://127.0.0.1:8080/"
+ "post.logout.redirect.uris":
"https://example.local/##https://example.k3s/"
}
},
{
diff --git
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
index 25614aee067..a5dac7ad693 100644
---
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
+++
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/AbstractOAuthProcessor.java
@@ -26,8 +26,6 @@ import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.apache.camel.oauth.OAuth.CAMEL_OAUTH_COOKIE;
-
public abstract class AbstractOAuthProcessor implements Processor {
protected final Logger log = LoggerFactory.getLogger(getClass());
@@ -71,11 +69,4 @@ public abstract class AbstractOAuthProcessor implements
Processor {
msg.setHeader("Location", redirectUrl);
msg.setBody("");
}
-
- protected void setSessionCookie(Message msg, OAuthSession session) {
- var sessionId = session.getSessionId();
- var cookie = "%s=%s; Path=/; HttpOnly; SameSite=None;
Secure".formatted(CAMEL_OAUTH_COOKIE, sessionId);
- msg.setHeader("Set-Cookie", cookie);
- log.debug("Set-Cookie: {}", cookie);
- }
}
diff --git
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
index 04ad43bc01a..22244eae6c9 100644
---
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
+++
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/InMemorySessionStore.java
@@ -24,6 +24,7 @@ import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.camel.Exchange;
+import org.apache.camel.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,6 +70,8 @@ public class InMemorySessionStore implements
OAuthSessionStore {
var msg = exchange.getMessage();
log.info("New OAuthSession: {}", sessionId);
+
+ setSessionCookie(msg, session);
msg.setHeader(OAuth.CAMEL_OAUTH_SESSION_ID, sessionId);
return session;
@@ -93,4 +96,15 @@ public class InMemorySessionStore implements
OAuthSessionStore {
return cookieMap;
}
+
+ private void setSessionCookie(Message msg, OAuthSession session) {
+ var sessionId = session.getSessionId();
+ var cookieId = "%s=%s".formatted(CAMEL_OAUTH_COOKIE, sessionId);
+ if (msg.getHeader("Set-Cookie") != null) {
+ throw new IllegalStateException("Duplicate 'Set-Cookie' header");
+ }
+ var cookie = cookieId + "; Path=/; HttpOnly; SameSite=None; Secure";
+ msg.setHeader("Set-Cookie", cookie);
+ log.debug("Set-Cookie: {}", cookie);
+ }
}
diff --git
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
index d18c429d492..5ad86652f14 100644
---
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
+++
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowCallback.java
@@ -66,9 +66,8 @@ public class OAuthCodeFlowCallback extends
AbstractOAuthProcessor {
postLoginUrl = postLoginUrl.substring(0, lastSlashIdx + 1);
}
- setSessionCookie(msg, session);
sendRedirect(msg, postLoginUrl);
- log.info("{} - Done", procName);
+ log.info("{} - Redirect to {}", procName, postLoginUrl);
}
}
diff --git
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
index 6a9cbf13f9e..537ca8188b2 100644
---
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
+++
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthCodeFlowProcessor.java
@@ -17,6 +17,7 @@
package org.apache.camel.oauth;
import org.apache.camel.Exchange;
+import org.apache.camel.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -49,23 +50,46 @@ public class OAuthCodeFlowProcessor extends
AbstractOAuthProcessor {
//
if (session.getUserProfile().isPresent()) {
authenticateExistingUserProfile(oauth, session);
+ log.info("{} - Done", procName);
}
// Fallback to the authorization code flow
//
if (session.getUserProfile().isEmpty()) {
- var postLoginUrl = msg.getHeader(Exchange.HTTP_URL, String.class);
- session.putValue("OAuthPostLoginUrl", postLoginUrl);
+ session.putValue("OAuthPostLoginUrl", getPostLoginUrl(msg));
var redirectUri = getRequiredProperty(exchange.getContext(),
CAMEL_OAUTH_REDIRECT_URI);
var params = new OAuthCodeFlowParams().setRedirectUri(redirectUri);
var authRequestUrl = oauth.buildCodeFlowAuthRequestUrl(params);
- setSessionCookie(msg, session);
sendRedirect(msg, authRequestUrl);
+ log.info("{} - Redirect to {}", procName, authRequestUrl);
}
+ }
- log.info("{} - Done", procName);
+ private String getPostLoginUrl(Message msg) {
+ String postLoginUrl;
+ var xProto = msg.getHeader("X-Forwarded-Proto", String.class);
+ var xHost = msg.getHeader("X-Forwarded-Host", String.class);
+ var xPort = msg.getHeader("X-Forwarded-Port", Integer.class);
+ if (xProto != null && xHost != null) {
+ postLoginUrl = xProto + "://" + xHost;
+ if (xPort != null) {
+ if (xProto.equals("https") && xPort != 443) {
+ postLoginUrl += ":" + xPort;
+ }
+ if (xProto.equals("http") && xPort != 80) {
+ postLoginUrl += ":" + xPort;
+ }
+ }
+ var httpPath = msg.getHeader(Exchange.HTTP_PATH, String.class);
+ if (httpPath != null && !httpPath.isEmpty()) {
+ postLoginUrl += httpPath;
+ }
+ } else {
+ postLoginUrl = msg.getHeader(Exchange.HTTP_URL, String.class);
+ }
+ return postLoginUrl;
}
}
diff --git
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
index d0aea8626dd..435086dc1e1 100644
---
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
+++
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/OAuthLogoutProcessor.java
@@ -26,6 +26,7 @@ public class OAuthLogoutProcessor extends
AbstractOAuthProcessor {
@Override
public void process(Exchange exchange) {
var context = exchange.getContext();
+ var msg = exchange.getMessage();
findOAuth(context).ifPresent(oauth -> {
@@ -41,10 +42,8 @@ public class OAuthLogoutProcessor extends
AbstractOAuthProcessor {
var logoutUrl = oauth.buildLogoutRequestUrl(params);
- log.info("OAuth logout: {}", logoutUrl);
- exchange.getMessage().setHeader(Exchange.HTTP_RESPONSE_CODE,
302);
- exchange.getMessage().setHeader("Location", logoutUrl);
- exchange.getMessage().setBody("");
+ log.info("{} - Logout, then {}", procName, postLogoutUrl);
+ sendRedirect(msg, logoutUrl);
});
});
}
diff --git
a/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
b/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
index 31fbfccdbe5..1f94f1cbab2 100644
---
a/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
+++
b/components/camel-oauth/src/test/java/org/apache/camel/test/oauth/SSLCertTrustTest.java
@@ -16,11 +16,17 @@
*/
package org.apache.camel.test.oauth;
+import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManagerFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
@@ -29,7 +35,37 @@ import org.junit.jupiter.api.Test;
class SSLCertTrustTest extends AbstractKeycloakTest {
@Test
- void testTrustedCertificate() {
+ void testCheckClusterCertificateTrust() throws Exception {
+
+ // Load certificate to check
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ FileInputStream fis = new FileInputStream("helm/etc/cluster.crt");
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
+
+ // Load default Java truststore
+ FileInputStream trustStream = new
FileInputStream(System.getProperty("java.home") + "/lib/security/cacerts");
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(trustStream, "changeit".toCharArray());
+
+ // Initialize TrustManager
+ TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(trustStore);
+
+ try {
+ for (var tm : tmf.getTrustManagers()) {
+ var xtm = (javax.net.ssl.X509TrustManager) tm;
+ xtm.checkServerTrusted(new X509Certificate[] { cert }, "RSA");
+ }
+ } catch (CertificateException ex) {
+ System.err.println("Untrusted, because of: " + ex);
+ return;
+ }
+
+ System.out.println("Trusted");
+ }
+
+ @Test
+ void testCheckKeycloakCertificateTrust() {
var admin = new KeycloakAdmin(new
KeycloakAdmin.AdminParams(KEYCLOAK_BASE_URL));
Assumptions.assumeTrue(admin.isKeycloakRunning(), "Keycloak is not
running");
Assertions.assertDoesNotThrow(() -> connectToUrl(KEYCLOAK_BASE_URL),
"Certificate should be trusted");
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
index cac96c85cec..58aec45a7f7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
@@ -103,9 +103,6 @@ The Camel JBang Kubernetes export command provides several
options to customize
|=======================================================================
|Option |Description
-|--trait-profile
-|The trait profile to use for the deployment.
-
|--service-account
|The service account used to run the application.
@@ -1135,8 +1132,6 @@ spec:
The Route trait enhances the Kubernetes manifest with a Route resource to
expose the application to the outside world. This requires the presence in the
Kubernetes manifest of a Service Resource.
-NOTE: You need to enable the OpenShift profile trait with
`--trait-profile=openshift` option.
-
The Route trait provides the following configuration options:
[cols="2m,1m,5a"]
@@ -1193,7 +1188,7 @@ You may specify these options with the export command to
customize the Route Res
[source,bash]
----
-camel kubernetes export Sample.java --trait-profile=openshift -t
route.enabled=true --trait route.host=example.com -t route.tls-termination=edge
-t route.tls-certificate=file:/tmp/tls.crt -t route.tls-key=file:/tmp/tls.key
+camel kubernetes export Sample.java --cluster-type=openshift -t
route.enabled=true --trait route.host=example.com -t route.tls-termination=edge
-t route.tls-certificate=file:/tmp/tls.crt -t route.tls-key=file:/tmp/tls.key
----
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
index e1eb76f4f20..62881712e38 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/IngressTrait.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits;
+import java.util.List;
import java.util.Optional;
import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath;
@@ -86,8 +87,9 @@ public class IngressTrait extends BaseTrait {
.endBackend()
.build();
+ var ingressHost =
Optional.ofNullable(ingressTrait.getHost()).orElse(DEFAULT_INGRESS_HOST);
IngressRule rule = new IngressRuleBuilder()
-
.withHost(Optional.ofNullable(ingressTrait.getHost()).orElse(DEFAULT_INGRESS_HOST))
+ .withHost(ingressHost)
.withNewHttp()
.withPaths(path)
.endHttp()
@@ -99,7 +101,10 @@ public class IngressTrait extends BaseTrait {
.withRules(rule)
.endSpec();
- if (ingressTrait.getTlsHosts() != null &&
ingressTrait.getTlsSecretName() != null) {
+ if (ingressTrait.getTlsSecretName() != null) {
+ if (ingressTrait.getTlsHosts() == null && !ingressHost.isEmpty()) {
+ ingressTrait.setTlsHosts(List.of(ingressHost));
+ }
IngressTLS tls = new IngressTLSBuilder()
.withHosts(ingressTrait.getTlsHosts())
.withSecretName(ingressTrait.getTlsSecretName())