Chapter 3. Securing the Undertow HTTP Server Abstract You can configure the built-in Undertow HTTP server to use SSL/TLS security by editing the contents of the etc/undertow.xml configuration file. In particular, you can add SSL/TLS security to the Fuse Console in this way. 3.1. Undertow server The Fuse container is pre-configured with an Undertow server, which acts as a general-purpose HTTP server and HTTP servlet container. Through a single HTTP port (by default, http://localhost:8181 ), the Undertow container can host multiple services, for example: Fuse Console (by default, http://localhost:8181/hawtio ) Apache CXF Web services endpoints (if the host and port are left unspecified in the endpoint configuration) Some Apache Camel endpoints If you use the default Undertow server for all of your HTTP endpoints, you can conveniently add SSL/TLS security to these HTTP endpoints by following the steps described here. 3.2. Create X.509 certificate and private key Before you can enable SSL/TLS on the Undertow server, you must create an X.509 certificate and private key, where the certificate and private key must be in Java keystore format (JKS format). For details of how to create a signed certificate and private key, see Appendix A, Managing Certificates . 3.3. Enabling SSL/TLS for Undertow in an Apache Karaf container For the following procedure, it is assumed that you have already created a signed X.509 certificate and private key pair in the keystore file, alice.ks , with keystore password, StorePass , and key password, KeyPass . To enable SSL/TLS for Undertow in a Karaf container: Make sure that the Pax Web server is configured to take its configuration from the etc/undertow.xml file. When you look at the contents of the etc/org.ops4j.pax.web.cfg file, you should see the following setting: Open the file, etc/org.ops4j.pax.web.cfg , in a text editor and add the following lines: Save and close the file, etc/org.ops4j.pax.web.cfg . Open the file, etc/undertow.xml , in a text editor. The steps assume you are working with the default undertow.xml file, unchanged since installation time. Search for the XML elements, http-listener and https-listener . Comment out the http-listener element (by enclosing it between <!-- and --> ) and uncomment the https-listener element (spread over two lines). The edited fragment of XML should now look something like this: <!-- HTTP(S) Listener references Socket Binding (and indirectly - Interfaces) --> <!-- http-listener name="http" socket-binding="http" /> --> verify-client: org.xnio.SslClientAuthMode.NOT_REQUESTED, org.xnio.SslClientAuthMode.REQUESTED, org.xnio.SslClientAuthMode.REQUIRED <!--<https-listener name="https" socket-binding="https" security-realm="https" verify-client="NOT_REQUESTED" enabled="true" /> --> <https-listener name="https" socket-binding="https" worker="default" buffer-pool="default" enabled="true" receive-buffer="65536" send-buffer="65536" tcp-backlog="128" tcp-keep-alive="false" read-timeout="-1" write-timeout="-1" max-connections="1000000" resolve-peer-address="false" disallowed-methods="TRACE OPTIONS" secure="true" max-post-size="10485760" buffer-pipelined-data="false" max-header-size="1048576" max-parameters="1000" max-headers="200" max-cookies="200" allow-encoded-slash="false" decode-url="true" url-charset="UTF-8" always-set-keep-alive="true" max-buffered-request-size="16384" record-request-start-time="true" allow-equals-in-cookie-value="false" no-request-timeout="60000" request-parse-timeout="60000" rfc6265-cookie-validation="false" allow-unescaped-characters-in-url="false" certificate-forwarding="false" proxy-address-forwarding="false" enable-http2="false" http2-enable-push="false" http2-header-table-size="4096" http2-initial-window-size="65535" http2-max-concurrent-streams="-1" http2-max-frame-size="16384" http2-max-header-list-size="-1" require-host-http11="false" proxy-protocol="false" security-realm="https" verify-client="NOT_REQUESTED" enabled-cipher-suites="TLS_AES_256_GCM_SHA384" enabled-protocols="TLSv1.3" ssl-session-cache-size="0" ssl-session-timeout="0" /> Search for the w:keystore element. By default, the w:keystore element is configured as follows: <w:keystore path="USD{karaf.etc}/certs/server.keystore" provider="JKS" alias="server" keystore-password="secret" key-password="secret" generate-self-signed-certificate-host="localhost" /> To install the alice certificate as the Undertow server's certificate, modify the w:keystore element attributes as follows: Set path to the absolute location of the alice.ks file on the file system. Set provider to JKS . Set alias to the alice certificate alias in the keystore. Set keystore-password to the value of the password that unlocks the key store. Set key-password to the value of the password that encrypts the alice private key. Delete the generate-self-signed-certificate-host attribute setting. For example, after installing the alice.ks keystore, the modified w:keystore element would look something like this: <w:keystore path="USD{karaf.etc}/certs/alice.ks" provider="JKS" alias="alice" keystore-password="StorePass" key-password="KeyPass" /> Search for the <interface name="secure"> tag, which is used to specify the IP addresses the secure HTTPS port binds to. By default, this element is commented out, as follows: <!--<interface name="secure">--> <!--<w:inet-address value="127.0.0.1" />--> <!--</interface>--> Uncomment the element and customize the value attribute to specify the IP address which the HTTPS port binds to. For example, the wildcard value, 0.0.0.0 , configures HTTPS to bind to all available IP addresses: <interface name="secure"> <w:inet-address value="0.0.0.0" /> </interface> Search for and uncomment the <socket-binding name="https" tag. When this tag is uncommented, it should look something like this: <socket-binding name="https" interface="secure" port="USD{org.osgi.service.http.port.secure}" /> Save and close the file, etc/undertow.xml . Restart the Fuse container, in order for the configuration changes to take effect. 3.4. Customizing allowed TLS protocols and cipher suites You can customize the allowed TLS protocols and cipher suites by modifying the following attributes of the w:engine element in the etc/undertow.xml file: enabled-cipher-suites Specifies the list of allowed TLS/SSL cipher suites. enabled-protocols Specifies the list of allowed TLS/SSL protocols. Warning Do not enable SSL protocol versions, as they are vulnerable to attack. Use only TLS protocol versions. For full details of the available protocols and cipher suites, consult the appropriate JVM documentation and security provider documentation. For example, for Java 8, see Java Cryptography Architecture Oracle Providers Documentation for JDK 8 . 3.5. Connect to the secure console After configuring SSL security for the Undertow server in the Pax Web configuration file, you should be able to open the Fuse Console by browsing to the following URL: Note Remember to type the https: scheme, instead of http: , in this URL. Initially, the browser will warn you that you are using an untrusted certificate. Skip this warning and you will be presented with the login screen for the Fuse Console. 3.6. Advanced Undertow configuration 3.6.1. IO configuration Since PAXWEB-1255 the configuration of the XNIO worker and buffer pool used by the listeners can be altered. In undertow.xml template there is a section that specifies default values of some IO-related parameters: The following buffer-pool parameters may be specified: buffer-size Specifies size of the buffer used for IO operations. When not specified, size is calculated depending on available memory. direct-buffers Determines whether java.nio.ByteBuffer#allocateDirect or java.nio.ByteBuffer#allocate should be used. The following worker parameters may be specified: io-threads The number of I/O threads to create for the worker. If not specified, the number of threads is set to the number of CPUs x 2. task-core-threads The number of threads for the core task thread pool. task-max-threads The maximum number of threads for the worker task thread pool. If not specified, the maximum number of threads is set to the number of CPUs x 16. 3.6.2. Worker IO configuration The Undertow thread pools and their names can be configured on a per-service or bundle basis which helps to make monitoring from Hawtio console and debugging more efficient. In the bundle blueprint configuration file (which is typically stored under the src/main/resources/OSGI-INF/blueprint directory in a Maven project), you can configure the workerIOName and ThreadPool as demonstrated in the following example. Example 3.1. httpu:engine-factory element with workerIOName and ThreadPool configuration The following threadingParameters may be specified: minThreads Specifies the number of "core" threads for the worker task thread pool. Generally this should be reasonably high, at least 10 per CPU core.. maxThreads Specifies the maximum number of threads for the worker task thread pool. The following worker parameters may be specified: workerIOThreads Specifies the number of I/O threads to create for the worker. If not specified, a default will be chosen. One IO thread per CPU core is a reasonable default. workerIOName Specifies the name for the worker. If not specified, the default "XNIO-1" will be chosen.

Commands:
org.ops4j.pax.web.config.file=USD{karaf.etc}/undertow.xml
org.osgi.service.http.port.secure=8443 org.osgi.service.http.secure.enabled=true
<!-- HTTP(S) Listener references Socket Binding (and indirectly - Interfaces) --> <!-- http-listener name="http" socket-binding="http" /> --> verify-client: org.xnio.SslClientAuthMode.NOT_REQUESTED, org.xnio.SslClientAuthMode.REQUESTED, org.xnio.SslClientAuthMode.REQUIRED <!--<https-listener name="https" socket-binding="https" security-realm="https" verify-client="NOT_REQUESTED" enabled="true" /> --> <https-listener name="https" socket-binding="https" worker="default" buffer-pool="default" enabled="true" receive-buffer="65536" send-buffer="65536" tcp-backlog="128" tcp-keep-alive="false" read-timeout="-1" write-timeout="-1" max-connections="1000000" resolve-peer-address="false" disallowed-methods="TRACE OPTIONS" secure="true" max-post-size="10485760" buffer-pipelined-data="false" max-header-size="1048576" max-parameters="1000" max-headers="200" max-cookies="200" allow-encoded-slash="false" decode-url="true" url-charset="UTF-8" always-set-keep-alive="true" max-buffered-request-size="16384" record-request-start-time="true" allow-equals-in-cookie-value="false" no-request-timeout="60000" request-parse-timeout="60000" rfc6265-cookie-validation="false" allow-unescaped-characters-in-url="false" certificate-forwarding="false" proxy-address-forwarding="false" enable-http2="false" http2-enable-push="false" http2-header-table-size="4096" http2-initial-window-size="65535" http2-max-concurrent-streams="-1" http2-max-frame-size="16384" http2-max-header-list-size="-1" require-host-http11="false" proxy-protocol="false" security-realm="https" verify-client="NOT_REQUESTED" enabled-cipher-suites="TLS_AES_256_GCM_SHA384" enabled-protocols="TLSv1.3" ssl-session-cache-size="0" ssl-session-timeout="0" />
<w:keystore path="USD{karaf.etc}/certs/server.keystore" provider="JKS" alias="server" keystore-password="secret" key-password="secret" generate-self-signed-certificate-host="localhost" />
<w:keystore path="USD{karaf.etc}/certs/alice.ks" provider="JKS" alias="alice" keystore-password="StorePass" key-password="KeyPass" />
<!--<interface name="secure">--> <!--<w:inet-address value="127.0.0.1" />--> <!--</interface>-->
<interface name="secure"> <w:inet-address value="0.0.0.0" /> </interface>
<socket-binding name="https" interface="secure" port="USD{org.osgi.service.http.port.secure}" />
https://localhost:8443/hawtio
<!-- Only "default" worker and buffer-pool are supported and can be used to override the default values used by all listeners buffer-pool: - buffer-size defaults to: - when < 64MB of Xmx: 512 - when < 128MB of Xmx: 1024 - when >= 128MB of Xmx: 16K - 20 - direct-buffers defaults to: - when < 64MB of Xmx: false - when >= 64MB of Xmx: true worker: - io-threads defaults to Math.max(Runtime.getRuntime().availableProcessors(), 2); - task-core-threads and task-max-threads default to io-threads * 8 --> <!-- <subsystem xmlns="urn:jboss:domain:io:3.0"> <buffer-pool name="default" buffer-size="16364" direct-buffers="true" /> <worker name="default" io-threads="8" task-core-threads="64" task-max-threads="64" task-keepalive="60000" /> </subsystem> -->
<httpu:engine-factory> <httpu:engine port="9001"> <httpu:threadingParameters minThreads="99" maxThreads="777" workerIOThreads="8" workerIOName="WorkerIOTest"/> </httpu:engine> </httpu:engine-factory>