Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ELY-1996] [Community] SSLContext to support delegation to alternate instances based on peer information. #1382

Merged
merged 2 commits into from Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion ELY_Messages.txt
Expand Up @@ -126,7 +126,7 @@
24000 - 24999 wildfly-elytron-jose-jwk
25000 - 25999 wildfly-elytron-jose-jws
26000 - 26999 wildfly-elytron-jose-util
27000 - 27999
27000 - 27999 wildfly-elytron-dynamic-ssl
28000 - 28999
29000 - 29999
30000 - 30999
Expand Down
1 change: 0 additions & 1 deletion auth/client/pom.xml
Expand Up @@ -76,7 +76,6 @@
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron-ssh-util</artifactId>
</dependency>

<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
Expand Down
@@ -0,0 +1,33 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.auth.client;

/**
* An interface indicating active sessions of an SSLContext
*/
public interface ActiveSessionsSSLContext {
/**
* Indicates if the SSLContext has active sessions.
*
* @return true if SSLContext has active sessions. Otherwise, false
*/
default boolean hasActiveSessions() {
return false;
}
}
Expand Up @@ -361,6 +361,10 @@ public <T, E extends Exception> T runAsSupplierEx(ExceptionSupplier<T, E> action
return runExFunction(ExceptionSupplier::get, action);
}

RuleNode<SecurityFactory<SSLContext>> getSslRules() {
return this.sslRules;
}

public ContextManager<AuthenticationContext> getInstanceContextManager() {
return getContextManager();
}
Expand Down
Expand Up @@ -30,7 +30,9 @@
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

Expand Down Expand Up @@ -196,6 +198,42 @@ private static AuthenticationConfiguration initializeConfiguration(final URI uri
return configuration;
}

/**
* Get all SSL contexts configured for this authentication context.
*
* @param authenticationContext the authentication context to examine (must not be {@code null})
fjuma marked this conversation as resolved.
Show resolved Hide resolved
* @return List of all configured SSL contexts belonging to the provided authentication context
*/
public List<SSLContext> getConfiguredSSLContexts(AuthenticationContext authenticationContext) throws GeneralSecurityException {
Assert.checkNotNullParam("authenticationContext", authenticationContext);
List<SSLContext> sslContexts = new ArrayList<>();
RuleNode<SecurityFactory<SSLContext>> node = authenticationContext.getSslRules();
while (node != null) {
sslContexts.add(node.getConfiguration().create());
node = node.getNext();
}
return sslContexts;
}

/**
* Get the default SSL context that should be used when no other rules match, or {@link SSLContext#getDefault()} if there is none configured.
*
* @param authenticationContext the authentication context to examine (must not be {@code null})
fjuma marked this conversation as resolved.
Show resolved Hide resolved
* @return the default SSL context configured if no other rules match
*/
public SSLContext getDefaultSSLContext(AuthenticationContext authenticationContext) throws GeneralSecurityException {
Assert.checkNotNullParam("authenticationContext", authenticationContext);
SSLContext defaultSSLContext = null;
RuleNode<SecurityFactory<SSLContext>> node = authenticationContext.getSslRules();
while (node != null) {
if (node.getRule().equals(MatchRule.ALL)) {
defaultSSLContext = node.getConfiguration().create();
}
node = node.getNext();
}
return defaultSSLContext == null ? SSLContext.getDefault() : defaultSSLContext;
}

/**
* Get the configured SSL context which matches ALL rules from provided AuthenticationContext, or {@link SSLContext#getDefault()} if there is none.
*
Expand Down
76 changes: 76 additions & 0 deletions dynamic-ssl/pom.xml
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron-parent</artifactId>
<version>2.3.2.CR1-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>wildfly-elytron-dynamic-ssl</artifactId>

<name>WildFly Elytron - Dynamic SSL</name>
<description>WildFly Security Dynamic SSL Implementation</description>
fjuma marked this conversation as resolved.
Show resolved Hide resolved
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron-client</artifactId>
</dependency>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wildfly.common</groupId>
<artifactId>wildfly-common</artifactId>
<scope>compile</scope>
</dependency>

<!--test dependencies-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- Mock Web Server -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wildfly.client</groupId>
<artifactId>wildfly-client-config</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,56 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.dynamic.ssl;

import org.wildfly.security.auth.client.ActiveSessionsSSLContext;

import javax.net.ssl.SSLContext;
import java.security.NoSuchAlgorithmException;

/**
* SSLContext that resolves which SSLContext to use based on peer's host and port information.
*
fjuma marked this conversation as resolved.
Show resolved Hide resolved
* @author <a href="mailto:dvilkola@redhat.com">Diana Krepinska</a>
*/
public final class DynamicSSLContext extends SSLContext implements ActiveSessionsSSLContext {

private static SSLContext resolverSSLContext(DynamicSSLContextSPI dynamicSSLContextSPIImpl) throws NoSuchAlgorithmException, DynamicSSLContextException {
return dynamicSSLContextSPIImpl.getConfiguredDefault() == null ?
SSLContext.getDefault() : dynamicSSLContextSPIImpl.getConfiguredDefault();
}

/**
* This constructor uses ServiceLoader to find provider of DynamicSSLContextSPI on classpath.
*/
public DynamicSSLContext() throws NoSuchAlgorithmException {
// this does not use provider and protocol from DynamicSSLContextSPI implementation found on classpath
// to avoid this ServiceLoader.load would have to be called 3 times in separate static method
super(new DynamicSSLContextSpiImpl(), SSLContext.getDefault().getProvider(), SSLContext.getDefault().getProtocol());
}

/**
* This constructor uses received DynamicSSLContextSPI implementation or finds it on classpath if received is null.
*
* @param dynamicSSLContextSPIImpl DynamicSSLContextSPI implementation to use. If null then ServiceLoader is used to locate it on classpath.
*/
public DynamicSSLContext(DynamicSSLContextSPI dynamicSSLContextSPIImpl) throws NoSuchAlgorithmException, DynamicSSLContextException {
fjuma marked this conversation as resolved.
Show resolved Hide resolved
super(new DynamicSSLContextSpiImpl(dynamicSSLContextSPIImpl),
resolverSSLContext(dynamicSSLContextSPIImpl).getProvider(),
resolverSSLContext(dynamicSSLContextSPIImpl).getProtocol());
}
}
@@ -0,0 +1,42 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.dynamic.ssl;

/**
* Exception to indicate a failure related to the DynamicSSLContext.
*
* @author <a href="mailto:dvilkola@redhat.com">Diana Krepinska</a>
*/
public class DynamicSSLContextException extends Exception {
private static final long serialVersionUID = 894798122053539237L;

public DynamicSSLContextException() {
}

public DynamicSSLContextException(String msg) {
super(msg);
}

public DynamicSSLContextException(String message, Throwable cause) {
super(message, cause);
}

public DynamicSSLContextException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,91 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.wildfly.security.dynamic.ssl;

import org.kohsuke.MetaInfServices;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.security.auth.client.AuthenticationContextConfigurationClient;

import javax.net.ssl.SSLContext;
import java.net.URI;
import java.security.AccessController;
import java.security.GeneralSecurityException;
import java.security.PrivilegedAction;
import java.util.List;

import static org.wildfly.common.Assert.checkNotNullParam;

/**
* Elytron client implementation of DynamicSSLContextSPI. It uses configuration from either provided instance of AuthenticationContext
* or from current AuthenticationContext if a configuration was not provided.
*
* @author <a href="mailto:dvilkola@redhat.com">Diana Krepinska (Vilkolakova)</a>
*/
@MetaInfServices(value = DynamicSSLContextSPI.class)
public class DynamicSSLContextImpl implements DynamicSSLContextSPI {

private final AuthenticationContextConfigurationClient AUTH_CONTEXT_CLIENT =
AccessController.doPrivileged((PrivilegedAction<AuthenticationContextConfigurationClient>) AuthenticationContextConfigurationClient::new);
private AuthenticationContext authenticationContext;
private SSLContext configuredDefaultSSLContext;
private List<SSLContext> configuredSSLContexts;

public DynamicSSLContextImpl() throws GeneralSecurityException {
}

public DynamicSSLContextImpl(AuthenticationContext authenticationContext) throws GeneralSecurityException {
checkNotNullParam("authenticationContext", authenticationContext);
this.authenticationContext = authenticationContext;
this.configuredSSLContexts = AUTH_CONTEXT_CLIENT.getConfiguredSSLContexts(authenticationContext);
this.configuredDefaultSSLContext = AUTH_CONTEXT_CLIENT.getDefaultSSLContext(authenticationContext);
}

@Override
public SSLContext getConfiguredDefault() throws DynamicSSLContextException {
if (this.configuredDefaultSSLContext != null) {
return this.configuredDefaultSSLContext;
}
try {
return AUTH_CONTEXT_CLIENT.getDefaultSSLContext(AuthenticationContext.captureCurrent());
} catch (GeneralSecurityException e) {
throw ElytronMessages.log.cannotObtainDefaultSSLContext(e);
}
}

@Override
public List<SSLContext> getConfiguredSSLContexts() throws DynamicSSLContextException {
if (this.configuredSSLContexts != null) {
return this.configuredSSLContexts;
}
try {
return AUTH_CONTEXT_CLIENT.getConfiguredSSLContexts(AuthenticationContext.captureCurrent());
} catch (GeneralSecurityException e) {
throw ElytronMessages.log.cannotObtainConfiguredSSLContexts(e);
}
}

@Override
public SSLContext getSSLContext(URI uri) throws DynamicSSLContextException {
try {
return AUTH_CONTEXT_CLIENT.getSSLContext(uri, authenticationContext == null ? AuthenticationContext.captureCurrent() : authenticationContext);
} catch (GeneralSecurityException e) {
throw ElytronMessages.log.cannotObtainSSLContextForGivenURI(e);
}
}
}