-
Notifications
You must be signed in to change notification settings - Fork 41.1k
Add SNI support to embedded web server SSL auto-configuration #26022
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
Comments
Thanks for the suggestion, @sokomishalov. How would you expect to configure the auto-configuration if it supported SNI? I'm wondering what you had imagined the configuration properties would be so that you can provide multiple key stores and map each to a host pattern. |
Thanks for the quick reply, @wilkinsona! server:
ssl:
enabled: true
key-store: /path/to/key-store
key-store-password: foobar
key-alias: fallback
sni:
- mapping: foo.example.com
ssl:
key-store: ${server.ssl.key-store}
key-store-password: ${server.ssl.key-store-password}
key-alias: foo
- mapping: bar.example.com
ssl:
key-store: ${server.ssl.key-store}
key-store-password: ${server.ssl.key-store-password}
key-alias: bar If you'd want to use SNI, you still have to set up a fallback SSL context in that rare case when the user's client does not support SNI. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
When will be supported then? |
@SidneyLann There's no commitment to the next feature release |
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.WebServerSslBundle;
import org.springframework.stereotype.Component;
import com.pcng.gateway.handler.SniSslServerCustomizer;
@Component
public class NettyServerSniCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
private static final String KEY_STORE = ".pc.com.p12";
private static final String SSL_FOLDER = "/home/sidney/app/ssl/";
@Value("${server.http2.enabled}")
private boolean enableHttp2;
@Override
public void customize(NettyReactiveWebServerFactory serverFactory) {
String domain2s = System.getProperty("domain2s");
String[] domain2sArr = domain2s.split(",");
String[] hostNames = new String[domain2sArr.length];
Ssl.ClientAuth[] clientAuths = new Ssl.ClientAuth[domain2sArr.length];
Http2[] http2s = new Http2[domain2sArr.length];
SslBundle[] sslBundles = new SslBundle[domain2sArr.length];
for (int i = 0; i < domain2sArr.length; i++) {
hostNames[i] = domain2sArr[i] + ".pc.com";
Ssl ssl = new Ssl();
ssl.setKeyStore(SSL_FOLDER + domain2sArr[i] + KEY_STORE);
ssl.setKeyStorePassword("mypassw0rd");
ssl.setKeyStoreType("PKCS12");
ssl.setClientAuth(Ssl.ClientAuth.NONE);
clientAuths[i] = ssl.getClientAuth();
Http2 http2 = new Http2();
http2.setEnabled(enableHttp2);
http2s[i] = http2;
sslBundles[i] = WebServerSslBundle.get(ssl);
}
try {
serverFactory.addServerCustomizers(new SniSslServerCustomizer(hostNames, http2s, clientAuths, sslBundles));
} catch (Exception e) {
e.printStackTrace();
}
}
} import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslOptions;
import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl;
import io.netty.handler.ssl.ClientAuth;
import reactor.netty.http.Http11SslContextSpec;
import reactor.netty.http.Http2SslContextSpec;
import reactor.netty.http.server.HttpServer;
import reactor.netty.tcp.AbstractProtocolSslContextSpec;
import reactor.netty.tcp.SslProvider;
public class SniSslServerCustomizer implements NettyServerCustomizer {
private final String[] hostNames;
private final Http2[] http2;
private final Ssl.ClientAuth[] clientAuth;
private final SslBundle[] sslBundle;
public SniSslServerCustomizer(String[] hostNames, Http2[] http2, Ssl.ClientAuth[] clientAuth, SslBundle[] sslBundle) {
this.hostNames = hostNames;
this.http2 = http2;
this.clientAuth = clientAuth;
this.sslBundle = sslBundle;
}
@Override
public HttpServer apply(HttpServer server) {
try {
AbstractProtocolSslContextSpec<?> sslContextSpec = null;
Map<String, Consumer<? super SslProvider.SslContextSpec>> domainMap = new HashMap<>();
for (int i = 1; i < sslBundle.length; i++) {
sslContextSpec = createSslContextSpec(i);
final AbstractProtocolSslContextSpec<?> sslContextSpec2 = sslContextSpec;
domainMap.put(this.hostNames[i], spec -> spec.sslContext(sslContextSpec2));
}
return server.secure(spec -> spec.sslContext(createSslContextSpec(0)).addSniMappings(domainMap));
} catch (Exception e) {
return null;
}
}
protected AbstractProtocolSslContextSpec<?> createSslContextSpec(int i) {
AbstractProtocolSslContextSpec<?> sslContextSpec = (this.http2[i] != null && this.http2[i].isEnabled()) ? Http2SslContextSpec.forServer(this.sslBundle[i].getManagers().getKeyManagerFactory())
: Http11SslContextSpec.forServer(this.sslBundle[i].getManagers().getKeyManagerFactory());
sslContextSpec.configure((builder) -> {
builder.trustManager(this.sslBundle[i].getManagers().getTrustManagerFactory());
SslOptions options = this.sslBundle[i].getOptions();
builder.protocols(options.getEnabledProtocols());
builder.ciphers(SslOptions.asSet(options.getCiphers()));
builder.clientAuth(org.springframework.boot.web.server.Ssl.ClientAuth.map(this.clientAuth[i], ClientAuth.NONE, ClientAuth.OPTIONAL, ClientAuth.REQUIRE));
});
return sslContextSpec;
}
} I have a spring boot 3 workaround now. |
It would be great and useful to have such functionality out-of-box.
I'm not sure if all web-server implementations support SNI, but tomcat and reactor-netty definitely support it.
The text was updated successfully, but these errors were encountered: