diff --git a/core-feature-pack/common/pom.xml b/core-feature-pack/common/pom.xml index e669b53d874..9c2eac5259e 100644 --- a/core-feature-pack/common/pom.xml +++ b/core-feature-pack/common/pom.xml @@ -322,6 +322,10 @@ org.wildfly.security wildfly-elytron-digest + + org.wildfly.security + wildfly-elytron-dynamic-ssl + org.wildfly.security wildfly-elytron-encryption diff --git a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml index 792b2bd42fa..8753b51d7e9 100644 --- a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml +++ b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml @@ -34,6 +34,7 @@ + diff --git a/elytron/pom.xml b/elytron/pom.xml index bf2df18ca9e..8832725e19d 100644 --- a/elytron/pom.xml +++ b/elytron/pom.xml @@ -64,6 +64,11 @@ wildfly-elytron-auth + + org.wildfly.security + wildfly-elytron-dynamic-ssl + + org.wildfly.security wildfly-elytron-realm-jdbc diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java b/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java index 7e602ea4d9f..0a2868ce658 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java @@ -15,6 +15,7 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_DOMAIN_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_FACTORY_CREDENTIAL_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SSL_CONTEXT_CAPABILITY; +import static org.wildfly.extension.elytron.ElytronDefinition.commonRequirements; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; import java.util.HashMap; @@ -42,6 +43,8 @@ import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.msc.service.ServiceBuilder; +import org.jboss.msc.service.ServiceController; +import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.value.InjectedValue; import org.wildfly.common.function.ExceptionSupplier; import org.wildfly.extension.elytron.TrivialService.ValueSupplier; @@ -491,6 +494,17 @@ protected ValueSupplier getValueSupplier(ServiceBuilder finalContext.apply(parentSupplier.get()); } + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + commonRequirements(installService(context, model)).setInitialMode(ServiceController.Mode.ON_DEMAND).install(); + } + + ServiceBuilder installService(OperationContext context, ModelNode model) throws OperationFailedException { + ServiceTarget serviceTarget = context.getCapabilityServiceTarget(); + ServiceBuilder serviceBuilder = context.getCapabilityServiceTarget().addCapability(AUTHENTICATION_CONTEXT_RUNTIME_CAPABILITY); + TrivialService authenticationContextTrivialService = new TrivialService(getValueSupplier((ServiceBuilder) serviceBuilder, context, model)); + return serviceTarget.addService(AUTHENTICATION_CONTEXT_RUNTIME_CAPABILITY.getCapabilityServiceName(context.getCurrentAddressValue()), authenticationContextTrivialService); + } }; return new TrivialResourceDefinition(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT, add, attributes, diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java index a21c73958f2..8d74e3c5cb4 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java @@ -294,6 +294,7 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(SSLDefinitions.getServerSNISSLContextDefinition()); resourceRegistration.registerSubModel(new CertificateAuthorityDefinition()); resourceRegistration.registerSubModel(new CertificateAuthorityAccountDefinition()); + resourceRegistration.registerSubModel(SSLDefinitions.getDynamicClientSSLContextDefinition()); // Credential Store Block resourceRegistration.registerSubModel(new CredentialStoreResourceDefinition()); diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java index 87b2310f58d..87fd07f4ae6 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java @@ -173,6 +173,8 @@ interface ElytronDescriptionConstants { String DISTINGUISHED_NAME = "distinguished-name"; String DISTRIBUTED_REALM = "distributed-realm"; String DOMAIN_NAMES = "domain-names"; + String DYNAMIC_CLIENT_SSL_CONTEXT = "dynamic-client-ssl-context"; + String DYNAMIC_CLIENT_SSL_CONTEXTS = "dynamic-client-ssl-contexts"; String ELYTRON_SECURITY = "elytron-security"; String ENABLE_CONNECTION_POOLING = "enable-connection-pooling"; diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java index b23e9200029..0fcd01f9820 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java @@ -26,6 +26,7 @@ import static org.wildfly.extension.elytron.ElytronDescriptionConstants.PERMISSION_SETS; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SECURITY_PROPERTY; import static org.wildfly.extension.elytron.PermissionMapperDefinitions.PERMISSIONS; +import static org.wildfly.extension.elytron.SSLDefinitions.getDynamicClientSSLContextDefinition; /** * Enumeration of elytron subsystem schema versions. @@ -191,7 +192,9 @@ private void addCredentialStoreParser(PersistentResourceXMLDescription.Persisten private void addTlsParser(PersistentResourceXMLDescription.PersistentResourceXMLBuilder builder) { TlsParser tlsParser = new TlsParser(); - if (this.since(ElytronSubsystemSchema.VERSION_14_0)) { + if (this.since(ElytronSubsystemSchema.VERSION_18_0_COMMUNITY) && this.enables(getDynamicClientSSLContextDefinition())) { + builder.addChild(tlsParser.tlsParserCommunity_18_0); + } else if (this.since(ElytronSubsystemSchema.VERSION_14_0)) { builder.addChild(tlsParser.tlsParser_14_0); } else if (this.since(ElytronSubsystemSchema.VERSION_12_0)) { builder.addChild(tlsParser.tlsParser_12_0); diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java index 1cb38056733..faa4b93f973 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java @@ -170,6 +170,8 @@ private static void from18(ChainedTransformationDescriptionBuilder chainedBuilde .setDiscard(DiscardAttributeChecker.UNDEFINED, EMIT_EVENTS) .addRejectCheck(new RejectAttributeChecker.SimpleRejectAttributeChecker(ModelNode.TRUE), IGNORE_UNAVAILABLE_REALMS) .addRejectCheck(RejectAttributeChecker.DEFINED, EMIT_EVENTS); + builder.rejectChildResource(PathElement.pathElement(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXTS)); + builder.rejectChildResource(PathElement.pathElement(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT)); } private static void from17(ChainedTransformationDescriptionBuilder chainedBuilder) { diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java b/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java index c9e5f8ad4ab..3fe5ebabcd3 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java @@ -21,6 +21,7 @@ import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceController.State; import org.wildfly.common.iteration.ByteIterator; +import org.wildfly.security.dynamic.ssl.DynamicSSLContext; /** * A {@link Resource} to represent a server-ssl-context/client-ssl-context, the majority is actually model @@ -129,6 +130,9 @@ public Resource clone() { */ private boolean hasActiveSessions() { final SSLContext sslContext = getSSLContext(sslContextServiceController); + if (sslContext instanceof DynamicSSLContext) { + return false; + } if (sslContext == null) return false; SSLSessionContext sslSessionContext = server ? sslContext.getServerSessionContext() : sslContext.getClientSessionContext(); return sslSessionContext.getIds().hasMoreElements(); diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java b/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java index 2a0615ecf7f..e46fbfc91da 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java @@ -8,6 +8,7 @@ import static org.jboss.as.controller.capability.RuntimeCapability.buildDynamicCapabilityName; import static org.jboss.as.controller.security.CredentialReference.handleCredentialReferenceUpdate; import static org.jboss.as.controller.security.CredentialReference.rollbackCredentialStoreUpdate; +import static org.wildfly.extension.elytron.Capabilities.AUTHENTICATION_CONTEXT_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.KEY_MANAGER_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.KEY_MANAGER_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.KEY_STORE_CAPABILITY; @@ -56,6 +57,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BooleanSupplier; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -98,6 +100,7 @@ import org.jboss.as.controller.security.CredentialReference; import org.jboss.as.controller.services.path.PathManager; import org.jboss.as.controller.services.path.PathManagerService; +import org.jboss.as.version.Stability; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.msc.service.ServiceBuilder; @@ -112,12 +115,16 @@ import org.wildfly.extension.elytron.TrivialService.ValueSupplier; import org.wildfly.extension.elytron._private.ElytronSubsystemMessages; import org.wildfly.extension.elytron.capabilities.PrincipalTransformer; +import org.wildfly.security.auth.client.AuthenticationContext; +import org.wildfly.security.dynamic.ssl.DynamicSSLContextImpl; import org.wildfly.security.auth.server.MechanismConfiguration; import org.wildfly.security.auth.server.MechanismConfigurationSelector; import org.wildfly.security.auth.server.RealmMapper; import org.wildfly.security.auth.server.SecurityDomain; import org.wildfly.security.credential.PasswordCredential; import org.wildfly.security.credential.source.CredentialSource; +import org.wildfly.security.dynamic.ssl.DynamicSSLContext; +import org.wildfly.security.dynamic.ssl.DynamicSSLContextException; import org.wildfly.security.keystore.AliasFilter; import org.wildfly.security.keystore.FilteringKeyStore; import org.wildfly.security.password.interfaces.ClearPassword; @@ -147,6 +154,13 @@ class SSLDefinitions { .setRestartAllServices() .build(); + static final SimpleAttributeDefinition AUTHENTICATION_CONTEXT_ATTRIBUTE = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT, ModelType.STRING, false) + .setMinSize(1) + .setRequired(true) + .setCapabilityReference(AUTHENTICATION_CONTEXT_CAPABILITY, SSL_CONTEXT_CAPABILITY) + .setRestartAllServices() + .build(); + static final SimpleAttributeDefinition PROVIDER_NAME = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PROVIDER_NAME, ModelType.STRING, true) .setAllowExpression(true) .setMinSize(1) @@ -1208,11 +1222,17 @@ public X509Certificate[] getAcceptedIssuers() { } private static ResourceDefinition createSSLContextDefinition(String pathKey, boolean server, AbstractAddStepHandler addHandler, AttributeDefinition[] attributes, boolean serverOrHostController) { + return createSSLContextDefinition(pathKey, server, addHandler, attributes, serverOrHostController, Stability.DEFAULT); + } + + private static ResourceDefinition createSSLContextDefinition(String pathKey, boolean server, AbstractAddStepHandler addHandler, AttributeDefinition[] attributes, boolean serverOrHostController, Stability stability) { + Builder builder = TrivialResourceDefinition.builder() .setPathKey(pathKey) .setAddHandler(addHandler) .setAttributes(attributes) - .setRuntimeCapabilities(SSL_CONTEXT_RUNTIME_CAPABILITY); + .setRuntimeCapabilities(SSL_CONTEXT_RUNTIME_CAPABILITY) + .setStability(stability); if (serverOrHostController) { builder.addReadOnlyAttribute(ACTIVE_SESSION_COUNT, new SSLContextRuntimeHandler() { @@ -1511,6 +1531,42 @@ protected void installedForResource(ServiceController serviceControl return createSSLContextDefinition(ElytronDescriptionConstants.CLIENT_SSL_CONTEXT, false, add, attributes, serverOrHostController); } + static ResourceDefinition getDynamicClientSSLContextDefinition() { + + AttributeDefinition[] attributes = new AttributeDefinition[]{AUTHENTICATION_CONTEXT_ATTRIBUTE}; + AbstractAddStepHandler add = new TrivialAddHandler(SSLContext.class, attributes, SSL_CONTEXT_RUNTIME_CAPABILITY) { + @Override + protected ValueSupplier getValueSupplier(ServiceBuilder serviceBuilder, OperationContext context, ModelNode model) throws OperationFailedException { + final String authenticationContextName = AUTHENTICATION_CONTEXT_ATTRIBUTE.resolveModelAttribute(context, model).asString(); + String authenticationContextCapability = buildDynamicCapabilityName(AUTHENTICATION_CONTEXT_CAPABILITY, authenticationContextName); + ServiceName acServiceName = context.getCapabilityServiceName(authenticationContextCapability, AuthenticationContext.class); + Supplier authenticationContextSupplier = serviceBuilder.requires(acServiceName); + + return () -> { + try { + return new DynamicSSLContext(new DynamicSSLContextImpl(authenticationContextSupplier.get())); + } catch (DynamicSSLContextException | GeneralSecurityException e) { + throw new RuntimeException(e); + } + }; + } + + @Override + protected Resource createResource(OperationContext context) { + SSLContextResource resource = new SSLContextResource(Resource.Factory.create(), false); + context.addResource(PathAddress.EMPTY_ADDRESS, resource); + return resource; + } + + @Override + protected void installedForResource(ServiceController serviceController, Resource resource) { + ((SSLContextResource) resource).setSSLContextServiceController(serviceController); + } + }; + + return createSSLContextDefinition(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, false, add, attributes, false, Stability.COMMUNITY); + } + private static Provider[] filterProviders(Provider[] all, String provider) { if (provider == null || all == null) return all; List list = new ArrayList<>(); diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java b/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java index 0aacf83fa57..cd8592d815e 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java @@ -13,6 +13,8 @@ import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CERTIFICATE_AUTHORITY_ACCOUNTS; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CLIENT_SSL_CONTEXT; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CLIENT_SSL_CONTEXTS; +import static org.wildfly.extension.elytron.ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT; +import static org.wildfly.extension.elytron.ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXTS; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.FILTERING_KEY_STORE; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HOST; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SNI_MAPPING; @@ -195,6 +197,10 @@ class TlsParser { .addAttribute(SSLDefinitions.PROVIDERS) .addAttribute(SSLDefinitions.PROVIDER_NAME); + private PersistentResourceXMLBuilder dynamicClientSslContextParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(DYNAMIC_CLIENT_SSL_CONTEXT)) + .setXmlWrapperElement(DYNAMIC_CLIENT_SSL_CONTEXTS) + .addAttribute(SSLDefinitions.AUTHENTICATION_CONTEXT_ATTRIBUTE); + private PersistentResourceXMLBuilder certificateAuthorityParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(CERTIFICATE_AUTHORITY)) .setXmlWrapperElement(CERTIFICATE_AUTHORITIES) .addAttribute(CertificateAuthorityDefinition.URL) @@ -349,4 +355,20 @@ public void marshallSingleElement(AttributeDefinition attribute, ModelNode mappi .addChild(certificateAuthorityAccountParser) .addChild(serverSslSniContextParser) .build(); + + final PersistentResourceXMLDescription tlsParserCommunity_18_0 = decorator(TLS) + .addChild(decorator(KEY_STORES) + .addChild(keyStoreParser) + .addChild(ldapKeyStoreParser) + .addChild(filteringKeyStoreParser) + ) + .addChild(keyManagerParser_12_0) + .addChild(trustManagerParser_14_0) + .addChild(serverSslContextParser_9_0) + .addChild(clientSslContextParser_9_0) + .addChild(certificateAuthorityParser) + .addChild(certificateAuthorityAccountParser) + .addChild(serverSslSniContextParser) + .addChild(dynamicClientSslContextParser) // new + .build(); } diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java b/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java index 533ad118376..a3599d89867 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java @@ -23,6 +23,7 @@ import org.jboss.as.controller.descriptions.ResourceDescriptionResolver; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.OperationEntry; +import org.jboss.as.version.Stability; /** * A trivial {@link ResourceDefinition} @@ -36,9 +37,11 @@ final class TrivialResourceDefinition extends SimpleResourceDefinition { private final Map readOnlyAttributes; private final List children; + private final Stability stability; + private TrivialResourceDefinition(String pathKey, ResourceDescriptionResolver resourceDescriptionResolver, AbstractAddStepHandler add, AbstractRemoveStepHandler remove, AttributeDefinition[] attributes, Map readOnlyAttributes, Map operations, List children, - RuntimeCapability[] runtimeCapabilities) { + RuntimeCapability[] runtimeCapabilities, Stability stability) { super(new Parameters(PathElement.pathElement(pathKey), resourceDescriptionResolver) .setAddHandler(add) @@ -51,14 +54,15 @@ private TrivialResourceDefinition(String pathKey, ResourceDescriptionResolver re this.readOnlyAttributes = readOnlyAttributes; this.operations = operations; this.children = children; + this.stability = stability; } TrivialResourceDefinition(String pathKey, ResourceDescriptionResolver resourceDescriptionResolver, AbstractAddStepHandler add, AttributeDefinition[] attributes, RuntimeCapability ... runtimeCapabilities) { - this(pathKey, resourceDescriptionResolver, add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities); + this(pathKey, resourceDescriptionResolver, add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities, Stability.DEFAULT); } TrivialResourceDefinition(String pathKey, AbstractAddStepHandler add, AttributeDefinition[] attributes, RuntimeCapability ... runtimeCapabilities) { - this(pathKey, ElytronExtension.getResourceDescriptionResolver(pathKey), add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities); + this(pathKey, ElytronExtension.getResourceDescriptionResolver(pathKey), add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities, Stability.DEFAULT); } @Override @@ -97,6 +101,11 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration } } + @Override + public Stability getStability() { + return this.stability; + } + public AttributeDefinition[] getAttributes() { return attributes; } @@ -116,6 +125,7 @@ static class Builder { private Map operations; private RuntimeCapability[] runtimeCapabilities; private List children; + private Stability stability = Stability.DEFAULT; Builder() {} @@ -173,6 +183,11 @@ Builder setRuntimeCapabilities(RuntimeCapability ... runtimeCapabilities) { return this; } + Builder setStability(Stability stability) { + this.stability = stability; + return this; + } + Builder addChild(ResourceDefinition child) { if (children == null) { children = new ArrayList<>(); @@ -187,7 +202,7 @@ ResourceDefinition build() { ResourceDescriptionResolver resourceDescriptionResolver = this.resourceDescriptionResolver != null ? this.resourceDescriptionResolver : ElytronExtension.getResourceDescriptionResolver(pathKey); return new TrivialResourceDefinition(pathKey, resourceDescriptionResolver, addHandler, removeHandler != null ? removeHandler : new TrivialCapabilityServiceRemoveHandler(addHandler, runtimeCapabilities), - attributes, readOnlyAttributes, operations, children, runtimeCapabilities); + attributes, readOnlyAttributes, operations, children, runtimeCapabilities, stability); } } diff --git a/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties b/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties index 64fa0e45c51..36d6297c7e5 100644 --- a/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties +++ b/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties @@ -1439,10 +1439,17 @@ elytron.client-ssl-context.ssl-session.peer-certificates.version=The certificate # Operations elytron.client-ssl-context.ssl-session.invalidate=Invalidate the SSLSession (Note: This does not terminate current connections, only prevents future connections from joining or resuming this session). +elytron.dynamic-client-ssl-context=A Dynamic SSL context for use on the client side of a connection. It makes it possible to choose the SSL context to use based on the peer's host and port information +# operations +elytron.dynamic-client-ssl-context.add=Add the Dynamic Client SSL context definition. +elytron.dynamic-client-ssl-context.remove=Remove the Dynamic Client SSL context definition. +#Attributes +elytron.dynamic-client-ssl-context.authentication-context=The authentication context that will be queried for SSL context based on peer information. + elytron.server-ssl-context=An SSLContext for use on the server side of a connection. # operations -elytron.server-ssl-context.add=Add the SSLContext definition. -elytron.server-ssl-context.remove=Remove the SSLContext definition. +elytron.server-ssl-context.add=Add the SSL context definition. +elytron.server-ssl-context.remove=Remove the SSL context definition. #Attributes elytron.server-ssl-context.security-domain=The security domain to use for authentication during SSL session establishment. elytron.server-ssl-context.cipher-suite-filter=The filter to apply to specify the enabled cipher suites for TLSv1.2 and below. diff --git a/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd b/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd index 6c4f3714355..3b7daeb7ff9 100644 --- a/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd +++ b/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd @@ -4779,6 +4779,7 @@ + @@ -6402,4 +6403,39 @@ + + + + + + Container for client dynamic SSL context definitions. + + + + + + + + + + + Definitions of a single client side dynamic SSL context. This context chooses SSL context based on peer's host and port information. + + + + + + The unique name of this client side dynamic SSL context. + + + + + + + The authentication context that will be used to query for rules when deciding which ssl context to use when connecting to a peer. + + + + + diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/DefaultStabilityTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/DefaultStabilityTestCase.java new file mode 100644 index 00000000000..fcad0e6e8a1 --- /dev/null +++ b/elytron/src/test/java/org/wildfly/extension/elytron/DefaultStabilityTestCase.java @@ -0,0 +1,60 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.wildfly.extension.elytron; + +import org.jboss.as.controller.client.helpers.ClientConstants; +import org.jboss.as.subsystem.test.AbstractSubsystemTest; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.as.version.Stability; +import org.jboss.dmr.ModelNode; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; + +public class DefaultStabilityTestCase extends AbstractSubsystemTest { + + private static final String DYNAMIC_SSL_CLIENT_CONTEXT_NAME = "dcsc"; + private static final String SUBSYSTEM = "subsystem"; + private static final String ELYTRON = "elytron"; + + public DefaultStabilityTestCase() { + super(ElytronExtension.SUBSYSTEM_NAME, new ElytronExtension(), Stability.DEFAULT); + } + + private static KernelServices services = null; + + @Before + public void initServices() throws Exception { + TestEnvironment testEnvironment = new TestEnvironment(Stability.DEFAULT); + services = super.createKernelServicesBuilder(testEnvironment).setSubsystemXmlResource("authentication-client.xml").build(); + if (!services.isSuccessfulBoot()) { + if (services.getBootError() != null) { + Assert.fail(services.getBootError().toString()); + } + Assert.fail("Failed to boot, no reason provided"); + } + } + + @Test + public void testAddDynamicClientSSLContextFailsInDefaultStability() { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.ADD); + operation.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).set("ac"); + ModelNode response = services.executeOperation(operation); + + if (!response.get(OUTCOME).asString().equals(FAILED)) { + Assert.fail(response.toJSONString(false)); + } + + if (!response.get("failure-description").asString().contains("No resource definition is registered for address")) { + Assert.fail(response.toJSONString(false)); + } + } +} \ No newline at end of file diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/DynamicSSLContextTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/DynamicSSLContextTestCase.java new file mode 100644 index 00000000000..ce6c6f380c8 --- /dev/null +++ b/elytron/src/test/java/org/wildfly/extension/elytron/DynamicSSLContextTestCase.java @@ -0,0 +1,151 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.wildfly.extension.elytron; + +import org.jboss.as.controller.client.helpers.ClientConstants; +import org.jboss.as.subsystem.test.AbstractSubsystemTest; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.as.version.Stability; +import org.jboss.dmr.ModelNode; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_REQUIRES_RELOAD; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESPONSE_HEADERS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DynamicSSLContextTestCase extends AbstractSubsystemTest { + + private static final String DYNAMIC_SSL_CLIENT_CONTEXT_NAME = "dcsc"; + private static final String SUBSYSTEM = "subsystem"; + private static final String ELYTRON = "elytron"; + + public DynamicSSLContextTestCase() { + super(ElytronExtension.SUBSYSTEM_NAME, new ElytronExtension(), Stability.COMMUNITY); + } + + private static KernelServices services = null; + + @Before + public void initServices() throws Exception { + TestEnvironment testEnvironment = new TestEnvironment(Stability.COMMUNITY); + services = super.createKernelServicesBuilder(testEnvironment).setSubsystemXmlResource("authentication-client.xml").build(); + if (!services.isSuccessfulBoot()) { + if (services.getBootError() != null) { + Assert.fail(services.getBootError().toString()); + } + Assert.fail("Failed to boot, no reason provided"); + } + } + + @Test + public void testAddDynamicClientSSLContext() { + addDynamicSSLClientContext(); + readDynamicSSLCientContextResource(); + } + + @Test + public void testRemoveDynamicClientSSLContext() { + addDynamicSSLClientContext(); + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.REMOVE_OPERATION); + assertSuccess(services.executeOperation(operation)); + + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION); + assertFailed(services.executeOperation(operation)); + } + + @Test + public void testUpdateDynamicClientSSLContext() { + addDynamicSSLClientContext(); + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION); + operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT); + operation.get(ClientConstants.VALUE).set("base"); + assertSuccess(services.executeOperation(operation)); + + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION); + ModelNode result = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT); + assertEquals("base", result.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).asString()); + } + + @Test + public void testAddDynamicClientSSLContextWithoutACThrowsEx() { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.ADD); + + ModelNode result = services.executeOperation(operation); + assertFailed(result); + String failureDescription = result.get(FAILURE_DESCRIPTION).asString(); + assertTrue(failureDescription.contains("'authentication-context' may not be null")); + } + + @Test + public void testAddDynamicClientSSLContextAsDefaultSSLContext() { + addDynamicSSLClientContext(); + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add(SUBSYSTEM, ELYTRON); + operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION); + operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.DEFAULT_SSL_CONTEXT); + operation.get(ClientConstants.VALUE).set(DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + ModelNode result = assertSuccess(services.executeOperation(operation)); + result.has(RESPONSE_HEADERS, OPERATION_REQUIRES_RELOAD); + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add(SUBSYSTEM, ELYTRON); + operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION); + result = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT); + assertEquals(DYNAMIC_SSL_CLIENT_CONTEXT_NAME, result.get(ElytronDescriptionConstants.DEFAULT_SSL_CONTEXT).asString()); + } + + private ModelNode assertSuccess(ModelNode response) { + if (!response.get(OUTCOME).asString().equals(SUCCESS)) { + Assert.fail(response.toJSONString(false)); + } + return response; + } + + private ModelNode assertFailed(ModelNode response) { + if (!response.get(OUTCOME).asString().equals(FAILED)) { + Assert.fail(response.toJSONString(false)); + } + return response; + } + + private void addDynamicSSLClientContext() { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.ADD); + operation.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).set("ac"); + assertSuccess(services.executeOperation(operation)); + } + + private void readDynamicSSLCientContextResource() { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR) + .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME); + operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION); + ModelNode result = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT); + assertEquals("ac", result.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).asString()); + } +} diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java b/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java index a9f34fa6b2e..ccd03216dc5 100644 --- a/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java +++ b/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java @@ -28,6 +28,7 @@ import org.jboss.as.subsystem.test.AdditionalInitialization; import org.jboss.as.subsystem.test.ControllerInitializer; import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.as.version.Stability; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.wildfly.security.x500.cert.BasicConstraintsExtension; @@ -44,6 +45,8 @@ class TestEnvironment extends AdditionalInitialization { private static final X500Principal ISSUER_DN = new X500Principal("O=Root Certificate Authority, EMAILADDRESS=elytron@wildfly.org, C=UK, ST=Elytron, CN=Elytron CA"); private static final X500Principal LOCALHOST_DN = new X500Principal("OU=Elytron, O=Elytron, C=CZ, ST=Elytron, CN=localhost"); + private static Stability stability; + private static KeyStore loadKeyStore() throws Exception{ KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, null); @@ -117,11 +120,25 @@ public static void setUpKeyStores() throws Exception { private final RunningMode runningMode; TestEnvironment() { - this(RunningMode.NORMAL); + this(RunningMode.NORMAL, Stability.DEFAULT); } TestEnvironment(RunningMode runningMode) { + this(runningMode, Stability.DEFAULT); + } + + TestEnvironment(Stability stability) { + this(RunningMode.NORMAL, stability); + } + + TestEnvironment(RunningMode runningMode, Stability stability) { this.runningMode = runningMode; + this.stability = stability; + } + + @Override + public Stability getStability() { + return stability; } @Override diff --git a/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml b/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml index 12308781830..54cfe836b00 100644 --- a/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml +++ b/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml @@ -44,6 +44,10 @@ + + + + @@ -53,4 +57,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 8d35344f81c..93bf2dcd520 100644 --- a/pom.xml +++ b/pom.xml @@ -1791,6 +1791,11 @@ wildfly-elytron-digest ${version.org.wildfly.security.elytron} + + org.wildfly.security + wildfly-elytron-dynamic-ssl + ${version.org.wildfly.security.elytron} + org.wildfly.security wildfly-elytron-encryption