Skip to content

Commit

Permalink
rpc: update OncRpcSvcBuilder to support SSLContext supplier
Browse files Browse the repository at this point in the history
Motivation:
SSLEngineConfigurator creates the SSLContext only once, thus
it host certificate is updated, we need to restart the service
to reload the certificate.

Modification:
Update OncRpcSvc to use custom versions of SSLEngineConfigurator, that
will always call SSLContextConfigurator#createSSLContext to get a
context. Create a custom version of SSLContextConfigurator, that uses
a suppler to get a SSLContext. Update OncRpcSvcBuilder to accept
SSLContext supplier.

Result:
More flexible SSLContext creation

Acked-by: Lea Morschel
Target: master
  • Loading branch information
kofemann committed May 7, 2024
1 parent 388014e commit 9375078
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 - 2018 Deutsches Elektronen-Synchroton,
* Copyright (c) 2009 - 2024 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -33,13 +33,21 @@
import org.glassfish.grizzly.memory.PooledMemoryManager;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.UDPNIOTransport;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.strategies.LeaderFollowerNIOStrategy;
import org.glassfish.grizzly.strategies.SameThreadIOStrategy;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;

import static com.google.common.base.Preconditions.checkArgument;

import static org.dcache.oncrpc4j.rpc.IoStrategy.WORKER_THREAD;

import javax.net.ssl.SSLContext;

import java.util.concurrent.Callable;
import javax.net.ssl.SSLEngine;

/**
* Class with utility methods for Grizzly
*/
Expand Down Expand Up @@ -165,4 +173,61 @@ public static IOStrategy getNIOStrategy(IoStrategy ioStrategy) {
return SameThreadIOStrategy.getInstance();
}
}

/**
* Create a SSLContextConfigurator that uses the specified SSLContext supplier.
* @param supplier a supplier that will be called to obtain the SSLContext instance.
* @return a SSLContextConfigurator that uses the specified SSLContext supplier.
*/
public static SSLContextConfigurator asContextConfigurator(Callable<SSLContext> supplier) {
return new SupplierBasedSSLContextConfigurator(supplier);
}

/**
* A version of {@link SSLContextConfigurator} that uses a supplier to obtain the SSLContext.
*/
private static class SupplierBasedSSLContextConfigurator extends SSLContextConfigurator {

private final Callable<SSLContext> contextSupplier;

public SupplierBasedSSLContextConfigurator(Callable<SSLContext> contextSupplier) {
super(false);
this.contextSupplier = contextSupplier;
}

@Override
public SSLContext createSSLContext(boolean throwException) {
try {
return contextSupplier.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

/**
* A SSLEngineConfigurator that always uses supplied sslContextConfigurator to create
* a new {@link SSLContext} instance.
*/
public static class ReloadableSSLEngineConfigurator extends SSLEngineConfigurator {

public ReloadableSSLEngineConfigurator(SSLContextConfigurator sslContextConfigurator, boolean clientMode,
boolean needClientAuth, boolean wantClientAuth) {
super(sslContextConfigurator, clientMode, needClientAuth, wantClientAuth);
}

@Override
public SSLContext getSslContext() {
return sslContextConfiguration.createSSLContext(true);
}

@Override
public SSLEngine createSSLEngine(String peerHost, int peerPort) {
var ctx = getSslContext();
final SSLEngine sslEngine = ctx.createSSLEngine(peerHost, peerPort);
configure(sslEngine);

return sslEngine;
}
}
}
28 changes: 21 additions & 7 deletions oncrpc4j-core/src/main/java/org/dcache/oncrpc4j/rpc/OncRpcSvc.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 - 2022 Deutsches Elektronen-Synchroton,
* Copyright (c) 2009 - 2024 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
Expand All @@ -20,6 +20,7 @@
package org.dcache.oncrpc4j.rpc;

import org.dcache.oncrpc4j.grizzly.GrizzlyRpcTransport;
import org.dcache.oncrpc4j.grizzly.GrizzlyUtils;
import org.dcache.oncrpc4j.grizzly.StartTlsFilter;
import org.dcache.oncrpc4j.portmap.GenericPortmapClient;
import org.dcache.oncrpc4j.portmap.OncPortmapClient;
Expand All @@ -45,6 +46,7 @@
import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;
import org.glassfish.grizzly.nio.transport.UDPNIOTransport;
import org.glassfish.grizzly.nio.transport.UDPNIOTransportBuilder;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
Expand All @@ -64,6 +66,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -107,7 +110,7 @@ public class OncRpcSvc {
/**
* SSL context to use, if configured.
*/
private final SSLContext _sslContext;
private final Callable<SSLContext> _sslContextProvider;

/**
* SSL parameters that should be applied to SSL engine.
Expand Down Expand Up @@ -194,8 +197,14 @@ public class OncRpcSvc {
_gssSessionManager = builder.getGssSessionManager();
_programs.putAll(builder.getRpcServices());
_withSubjectPropagation = builder.getSubjectPropagation();
_svcName = builder.getServiceName();
_sslContext = builder.getSSLContext();
_svcName = builder.getServiceName();

if (builder.getSSLContext() != null) {
final SSLContext sslContext = builder.getSSLContext();
_sslContextProvider = () -> sslContext;
} else {
_sslContextProvider = builder.getSSLContextProvider();
}
_startTLS = builder.isStartTLS();
_sslParams = builder.getSSLParameters();
_callInterceptor = builder.getCallInterceptor();
Expand Down Expand Up @@ -323,12 +332,17 @@ public void start() throws IOException {

FilterChainBuilder filterChain = FilterChainBuilder.stateless();
filterChain.add(new TransportFilter());
if (_sslContext != null) {
if (_sslContextProvider != null) {

SSLContextConfigurator sslContextConfigurator = GrizzlyUtils.asContextConfigurator(_sslContextProvider);

// Grizzly original SSLEngineConfigurator reuses the context. We need to use the one
// that sslContextConfigurator#createSSLContext returns to support certificate reloading.
SSLEngineConfigurator serverSSLEngineConfigurator =
new SSLEngineConfigurator(_sslContext, false, false, false);
new GrizzlyUtils.ReloadableSSLEngineConfigurator(sslContextConfigurator, false, false, false);

SSLEngineConfigurator clientSSLEngineConfigurator =
new SSLEngineConfigurator(_sslContext, true, false, false);
new GrizzlyUtils.ReloadableSSLEngineConfigurator(sslContextConfigurator, true, false, false);

if (_sslParams != null) {
String[] cipherSuites = _sslParams.getCipherSuites();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
Expand Down Expand Up @@ -81,6 +82,7 @@ public class OncRpcSvcBuilder {
private int _workerThreadPoolSize = 0;
private boolean _subjectPropagation = false;
private SSLContext _sslContext = null;
private Callable<SSLContext> _sslContextProvider = null;
private boolean _startTLS = false;
private SSLParameters _sslParams;
private MemoryAllocator _allocator = MemoryAllocator.DEFAULT;
Expand Down Expand Up @@ -236,6 +238,15 @@ public OncRpcSvcBuilder withMemoryAllocator(MemoryAllocator allocator) {
return this;
}

public OncRpcSvcBuilder withSSLContextProvider(Callable<SSLContext> sslContextProvider) {
_sslContextProvider = sslContextProvider;
return this;
}

public Callable<SSLContext> getSSLContextProvider() {
return _sslContextProvider;
}

public boolean getSubjectPropagation() {
return _subjectPropagation;
}
Expand Down Expand Up @@ -359,6 +370,10 @@ public OncRpcSvc build() {
throw new IllegalArgumentException("Can't set worker thread pool size with external execution service");
}

if (_sslContext != null && _sslContextProvider != null) {
throw new IllegalArgumentException("Can't set both SSLContext and SSLContextProvider");
}

return new OncRpcSvc(this);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 - 2018 Deutsches Elektronen-Synchroton,
* Copyright (c) 2009 - 2024 Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY
*
* This library is free software; you can redistribute it and/or modify
Expand All @@ -21,13 +21,17 @@

import org.dcache.oncrpc4j.rpc.OncRpcSvc;
import org.dcache.oncrpc4j.rpc.OncRpcSvcBuilder;

import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;

import static org.mockito.Mockito.mock;
import static org.junit.Assert.*;

import javax.net.ssl.SSLContext;

/**
*
*/
Expand Down Expand Up @@ -105,4 +109,12 @@ public void shouldThrowExceptionDefinedWorkerThreadPoolWithExtern() {
.build();
}

@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionIfSSLContextAndProvidedDefined() throws NoSuchAlgorithmException {
new OncRpcSvcBuilder()
.withTCP()
.withSSLContext(SSLContext.getDefault())
.withSSLContextProvider(() -> SSLContext.getDefault())
.build();
}
}

0 comments on commit 9375078

Please sign in to comment.