diff --git a/x-pack/plugin/security/build.gradle b/x-pack/plugin/security/build.gradle index 59779dfd7703f..44e3c49c6c5db 100644 --- a/x-pack/plugin/security/build.gradle +++ b/x-pack/plugin/security/build.gradle @@ -81,6 +81,7 @@ dependencies { // Dependencies for oidc api "com.nimbusds:oauth2-oidc-sdk:11.10.1" api "com.nimbusds:nimbus-jose-jwt:9.37.3" + api project(xpackModule('security:lib:jose-wrapper')) api "com.nimbusds:lang-tag:1.4.4" api "com.sun.mail:jakarta.mail:1.6.3" api "net.jcip:jcip-annotations:1.0" diff --git a/x-pack/plugin/security/lib/build.gradle b/x-pack/plugin/security/lib/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/plugin/security/lib/jose-wrapper/build.gradle b/x-pack/plugin/security/lib/jose-wrapper/build.gradle new file mode 100644 index 0000000000000..39144b359312b --- /dev/null +++ b/x-pack/plugin/security/lib/jose-wrapper/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'elasticsearch.build' + +base { + archivesName = 'elasticsearch-jose-wrapper' +} + +dependencies { + api "com.nimbusds:nimbus-jose-jwt:9.37.3" + api project(':server') +} diff --git a/x-pack/plugin/security/lib/jose-wrapper/src/main/java/module-info.java b/x-pack/plugin/security/lib/jose-wrapper/src/main/java/module-info.java new file mode 100644 index 0000000000000..388121e4afd2d --- /dev/null +++ b/x-pack/plugin/security/lib/jose-wrapper/src/main/java/module-info.java @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module org.elasticsearch.jose { + requires org.elasticsearch.server; + requires com.nimbusds.jose.jwt; + exports org.elasticsearch.jose; +} diff --git a/x-pack/plugin/security/lib/jose-wrapper/src/main/java/org/elasticsearch/jose/JoseWrapper.java b/x-pack/plugin/security/lib/jose-wrapper/src/main/java/org/elasticsearch/jose/JoseWrapper.java new file mode 100644 index 0000000000000..d94fff459bab2 --- /dev/null +++ b/x-pack/plugin/security/lib/jose-wrapper/src/main/java/org/elasticsearch/jose/JoseWrapper.java @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.jose; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import org.elasticsearch.SpecialPermission; + + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class wraps the operations requiring access in {@link AccessController#doPrivileged(PrivilegedAction)} blocks. + * Can't do these operations inline with giving too much access due to how the security manager calculates the stack for lambda expressions. + * Isolating the calls here allows for least privilege access to this helper jar. + */ +public class JoseWrapper { + + // utility class + private JoseWrapper() {} + + public static String getHeaderAsString(SignedJWT signedJWT) { + SpecialPermission.check(); + return AccessController.doPrivileged((PrivilegedAction) () -> signedJWT.getHeader().toString()); + + } + + public static String getClaimsSetAsString(JWTClaimsSet jwtClaimsSet) { + SpecialPermission.check(); + return AccessController.doPrivileged((PrivilegedAction) jwtClaimsSet::toString); + } +} diff --git a/x-pack/plugin/security/src/main/java/module-info.java b/x-pack/plugin/security/src/main/java/module-info.java index a072b34da7e96..3377e187a383e 100644 --- a/x-pack/plugin/security/src/main/java/module-info.java +++ b/x-pack/plugin/security/src/main/java/module-info.java @@ -49,6 +49,7 @@ requires oauth2.oidc.sdk; requires org.slf4j; requires unboundid.ldapsdk; + requires org.elasticsearch.jose; exports org.elasticsearch.xpack.security.action to org.elasticsearch.server; exports org.elasticsearch.xpack.security.action.apikey to org.elasticsearch.server; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtRealm.java index 7613e7b3972af..3cba7c8642cc8 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtRealm.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.jose.JoseWrapper; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; @@ -257,14 +258,15 @@ public void authenticate(final AuthenticationToken authenticationToken, final Ac } processValidatedJwt(tokenPrincipal, jwtCacheKey, claimsSet, listener); }, ex -> { + final String msg = "Realm [" + name() + "] JWT validation failed for token=[" + tokenPrincipal + "] with header [" - + jwtAuthenticationToken.getSignedJWT().getHeader() + + JoseWrapper.getHeaderAsString(jwtAuthenticationToken.getSignedJWT()) + "] and claimSet [" - + jwtAuthenticationToken.getJWTClaimsSet() + + JoseWrapper.getClaimsSetAsString(jwtAuthenticationToken.getJWTClaimsSet()) + "]"; if (logger.isTraceEnabled()) { diff --git a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.codebases b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.codebases index 94cfaec2d519c..12c64c29577c5 100644 --- a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.codebases +++ b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.codebases @@ -1,2 +1,4 @@ netty-common: io.netty.util.NettyRuntime netty-transport: io.netty.channel.Channel +nimbus-jose-jwt: com.nimbusds.jose.shaded.gson.internal.ConstructorConstructor +oauth2-oidc-sdk: com.nimbusds.jwt.SignedJWT diff --git a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy index 2c9d38e5ae55e..a6546320ed71d 100644 --- a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy +++ b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy @@ -51,3 +51,20 @@ grant codeBase "${codebase.netty-transport}" { // the bug says it only happened rarely, and that its fixed, but apparently it still happens rarely! permission java.util.PropertyPermission "sun.nio.ch.bugLevel", "write"; }; + +grant codeBase "${codebase.oauth2-oidc-sdk}" { + // for JSON serialization based on a shaded GSON dependency + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; +}; + +grant codeBase "${codebase.nimbus-jose-jwt}" { + // for JSON serialization based on a shaded GSON dependency + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; +}; + +grant codeBase "${codebase.elasticsearch-jose-wrapper}" { + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; +};