Server Side SNI Matching with WildFly
For quite some time now, WildFly has provided support for server side Server Name Indication (SNI) matching. This allows a WildFly instance with multiple virtual hosts that share a single IP address to present the correct certificate depending on the server name specified by the client via the SNI extension. This blog post is going to go through a complete example to show how to configure this.
Example Project
First, clone the elytron-examples
repo locally:
git clone https://github.com/wildfly-security-incubator/elytron-examples
cd elytron-examples
A WildFly CLI script that contains all of the commands used in this example can be found in the
server-ssl-sni-context
project in the elytron-examples
git repository.
Virtual Host Configuration
We’re going to configure a WildFly instance with two virtual hosts: app1.com
and app2.com
. The first thing
we’re going to do is add a couple aliases for localhost in our /etc/hosts/
file:
127.0.0.1 app1.com
127.0.0.1 app2.com
Next, we’re going to add the following virtual host configuration to the Undertow subsystem:
/subsystem=undertow/server=default-server/host=app1:add(alias=["app1.com"],default-web-module=app1.war)
/subsystem=undertow/server=default-server/host=app2:add(alias=["app2.com"],default-web-module=app2.war)
We’re now going to deploy the two web applications from our server-ssl-sni-context
example project:
cd $PATH_TO_ELYTRON_EXAMPLES/server-ssl-sni-context/app1
mvn wildfly:deploy
cd $PATH_TO_ELYTRON_EXAMPLES/server-ssl-sni-context/app2
mvn wildfly:deploy
We can now access our two virtual hosts using http://app1.com:8080 and http://app2.com:8080. In the next few sections, we’re going to go through the steps needed to configure one-way SSL and provide support for SNI matching.
Certificate Generation
We need a certificate for each virtual host that we have configured. Normally, you would obtain a signed certificate from a trusted certificate authority for each virtual host. For this example, we’re simply going to generate and make use of self-signed certificates as shown in the following commands:
/subsystem=elytron/key-store=serverKS:generate-key-pair(alias=app1,distinguished-name="CN=app1.com",algorithm=RSA)
/subsystem=elytron/key-store=serverKS:generate-key-pair(alias=app2,distinguished-name="CN=app2.com",algorithm=RSA)
/subsystem=elytron/key-store=serverKS:generate-key-pair(alias=localhost,distinguished-name="CN=localhost",algorithm=RSA)
Notice that we now have three different certificates: one that we want to use for app1.com
, one that we want to use for
app2.com
, and one that we want to use for localhost
.
SNI Matching Configuration
Now that we have configured our server keystore, our next step is to generate a few key managers that will be used to select each of the above certificates:
/subsystem=elytron/key-manager=app1KM:add(key-store=serverKS,credential-reference={clear-text=secret},alias-filter=app1)
/subsystem=elytron/key-manager=app2KM:add(key-store=serverKS,credential-reference={clear-text=secret},alias-filter=app2)
/subsystem=elytron/key-manager=localhostKM:add(key-store=serverKS,credential-reference={clear-text=secret},alias-filter=localhost)
Notice that app1KM
will always select the certificate we want to use for app1.com
. Similarly, app2KM
will always
select the certificate we want to use for app2.com
. Finally, localhostKM
will always select the certificate we want to use
for localhost
.
Next, we’re going to configure some server SSL contexts that make use of each of the above key managers:
/subsystem=elytron/server-ssl-context=app1SSC:add(key-manager=app1KM)
/subsystem=elytron/server-ssl-context=app2SSC:add(key-manager=app2KM)
/subsystem=elytron/server-ssl-context=localhostSSC:add(key-manager=localhostKM)
As an example, if app1SSC
is in use, it will make use of the app1KM
key manager which means that it will
select the certificate we want to use for app1.com
.
Finally, we can now configure a server SSL SNI context that makes use of our server SSL contexts:
/subsystem=elytron/server-ssl-sni-context=sniSSC:add(default-ssl-context=localhostSSC, host-context-map={app1\\.com=app1SSC,app2\\.com=app2SSC})
The above server SSL SNI context will ensure the correct certificate is presented to the client based on the SNI
extension provided by the client. If the client does not specify this extension or if the value does not match
any of the hostnames from our host-context-map
, then the default-ssl-context
will be used.
We’re now going to update our HTTPS listener configuration in the Undertow subsystem so that it references our server SSL SNI context:
batch
/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm)
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=sniSSC)
run-batch
reload
Access app1.com and app2.com via HTTPS
Now that we’ve enabled server side SNI matching, we can use our browser to access app1.com
and app2.com
via HTTPS. Let’s first try to access https://app1.com:8443. Note that the browser is going to add the SNI
extension with value app1.com
in the ClientHello message it sends to the server at the beginning of the
TLS handshake. Now, since the server’s certificate is self-signed, it won’t be trusted by your browser.
You’ll need to manually confirm that this certificate is trusted or configure your browser to trust it.
Inspect the certificate that was presented by the server. Notice that it’s the certificate for app1.com
.
Next, try accessing https://app2.com:8443. Inspect the certificate that was presented by the server. Notice
that this time, it’s the certificate for app2.com
.
Finally, try accessing https://localhost:8443. This time, the certificate presented by the server will be
the certificate for localhost
.
Summary
This blog post has shown how to configure server side SNI matching for WildFly. For more details, take a look at the Elytron documentation.