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.

Comment From: wilkinsona

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.

Comment From: sokomishalov

Thanks for the quick reply, @wilkinsona! Well, I suppose it could look like that:

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. I'm not sure if it's ok to reuse SSL properties in that recursive way or just to provide new higher-level property for SNI mappings inside ServerProperties, but I think it could be possible to reuse Ssl-properties POJO somehow. Also, maybe it could be useful to provide a property that will automatically extract DNS/IP from SAN or just CN to provide default SNI-mapping for this SSL context. Here is my gist - netty customizer to create SNI from a single key-store with multiple aliases with current spring-boot SSL properties, which maps SSL context to domain names from SAN and/or CN. Maybe someone will find it useful.

Comment From: wilkinsona

@SidneyLann Please use a thumbs up reaction (👍) on the opening description rather than commenting as it avoids notifying everyone watching the repository.

Comment From: SidneyLann

server.ssl.enabled=true server.ssl.key-store=/home/sidney/app/ssl/pc9g.com.p12 server.ssl.key-store-password=password server.ssl.key-store-type=PKCS12 server.ssl.sni[0].mapping=gsz.pc8g.com server.ssl.sni[0].ssl.key-store=/home/sidney/app/ssl/gsz.pc9g.com.p12 server.ssl.sni[0].ssl.key-store-password=password

Why the settings of sni not work? exception accour: javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

Comment From: wilkinsona

@SidneyLann This issue is still open as SNI is not yet supported.

Comment From: SidneyLann

When will be supported then?

Comment From: snicoll

@SidneyLann There's no commitment to the next feature release 2.7.x as the milestone on the issue indicates (2.x at the moment). If someone contributes a PR, we might get to it sooner.

Comment From: SidneyLann

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.