Configure HTTPS for jetty-maven-plugin 9.0.x

For the impatient

If you do not want to know why it does not work and how I fixed it, just jump to the quick fix!

jetty-maven-plugin 9.0.x breaks the HTTPS-Connector

With Jetty 9.0.x the configuration of the jetty-maven-plugin (formaly known as maven-jetty-plugin) has changed dramatically. Since then, it is no more possible to configure a HTTPS-Connector in the plugin easily. Normally, connecting your development-container via HTTPS was not often necessary. But since Snowden, encryption is on everybodys mind. And so, testing the encrypted part of your webapp becomes more and more important.

Why it is “broken” in jetty-maven-plugin 9.0.x

A bug-report stats, that Since the constructor signature changed for Connectors in jetty-9 to require the Server instance to be passed into it, it is no longer possible to configure Connectors directly with the plugin (because maven requires no-arg constructor for any <configuration> elements).

The documentation includes an example, how to configure a HTTPS Connector with the help of a jetty.xml-file. But unfortunatly, this example is broken. Jetty refuses to start with the following error: [ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.0.5.v20130815:run (default-cli) on project FOOBAR: Failure: Unknown configuration type: New in org.eclipse.jetty.xml.XmlConfiguration@4809f93a -> [Help 1].

Get HTTPS running again

So, here is, what you have to do to fix this broken example: the content shown for the file jetty.xml in the example is wrong. It has to look like the other example-files. That is, ith has to start with a <Configure>-tag. The corrected content of the file looks like this:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">

<!-- ============================================================= -->
<!-- Configure the Http Configuration                              -->
<!-- ============================================================= -->
<Configure id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
  <Set name="secureScheme">https</Set>
  <Set name="securePort"><Property name="jetty.secure.port" default="8443" /></Set>
  <Set name="outputBufferSize">32768</Set>
  <Set name="requestHeaderSize">8192</Set>
  <Set name="responseHeaderSize">8192</Set>
  <Set name="sendServerVersion">true</Set>
  <Set name="sendDateHeader">false</Set>
  <Set name="headerCacheSize">512</Set>

  <!-- Uncomment to enable handling of X-Forwarded- style headers
  <Call name="addCustomizer">
    <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
  </Call>
  -->
</Configure>

But it’s not running!

If you are getting the error [ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.0.5.v20130815:run (default-cli) on project FOOBAR: Failure: etc/jetty.keystore (file or directory not found) -> [Help 1] now, this is because you have to create/get a certificate for your HTTPS-Connector. For development, a selfsigned certificate is sufficient. You can easily create one like back in the good old maven-jetty-plugin-times, with this command: keytool -genkey -alias jetty -keyalg RSA -keystore src/test/resources/jetty.keystore -storepass secret -keypass secret -dname "CN=localhost". Just be sure, to change the example file jetty-ssl.xml, to reflect the path to your new keystore file and password. Your jetty-ssl.xml should look like:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">

<!-- ============================================================= -->
<!-- Configure a TLS (SSL) Context Factory                         -->
<!-- This configuration must be used in conjunction with jetty.xml -->
<!-- and either jetty-https.xml or jetty-spdy.xml (but not both)   -->
<!-- ============================================================= -->
<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
  <Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.keystore" default="src/test/resources/jetty.keystore"/></Set>
  <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="secret"/></Set>
  <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="secret"/></Set>
  <Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.truststore" default="src/test/resources/jetty.keystore"/></Set>
  <Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="secret"/></Set>
  <Set name="EndpointIdentificationAlgorithm"></Set>
  <Set name="ExcludeCipherSuites">
    <Array type="String">
      <Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
      <Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
      <Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
      <Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
      <Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
      <Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
      <Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
    </Array>
  </Set>

  <!-- =========================================================== -->
  <!-- Create a TLS specific HttpConfiguration based on the        -->
  <!-- common HttpConfiguration defined in jetty.xml               -->
  <!-- Add a SecureRequestCustomizer to extract certificate and    -->
  <!-- session information                                         -->
  <!-- =========================================================== -->
  <New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
    <Arg><Ref refid="httpConfig"/></Arg>
    <Call name="addCustomizer">
      <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
    </Call>
  </New>

</Configure>

But it’s still not running!

Unless you are running mvn jetty:run as root, you should see another error now: [ERROR] Failed to execute goal org.eclipse.jetty:jetty-maven-plugin:9.0.5.v20130815:run (default-cli) on project FOOBAR: Failure: Permission denied -> [Help 1]. This is, because the ports are set to the numbers 80 and 443 of the privileged port-range.

You have to change jetty-http.xml like this:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">

<!-- ============================================================= -->
<!-- Configure the Jetty Server instance with an ID "Server"       -->
<!-- by adding a HTTP connector.                                   -->
<!-- This configuration must be used in conjunction with jetty.xml -->
<!-- ============================================================= -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <!-- =========================================================== -->
  <!-- Add a HTTP Connector.                                       -->
  <!-- Configure an o.e.j.server.ServerConnector with a single     -->
  <!-- HttpConnectionFactory instance using the common httpConfig  -->
  <!-- instance defined in jetty.xml                               -->
  <!--                                                             -->
  <!-- Consult the javadoc of o.e.j.server.ServerConnector and     -->
  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
  <!-- that may be set here.                                       -->
  <!-- =========================================================== -->
  <Call name="addConnector">
    <Arg>
      <New class="org.eclipse.jetty.server.ServerConnector">
        <Arg name="server"><Ref refid="Server" /></Arg>
        <Arg name="factories">
          <Array type="org.eclipse.jetty.server.ConnectionFactory">
            <Item>
              <New class="org.eclipse.jetty.server.HttpConnectionFactory">
                <Arg name="config"><Ref refid="httpConfig" /></Arg>
              </New>
            </Item>
          </Array>
        </Arg>
        <Set name="host"><Property name="jetty.host" /></Set>
        <Set name="port"><Property name="jetty.port" default="8080" /></Set>
        <Set name="idleTimeout"><Property name="http.timeout" default="30000"/></Set>
      </New>
    </Arg>
  </Call>

</Configure>

… and jetty-https.xml like this:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">

<!-- ============================================================= -->
<!-- Configure a HTTPS connector.                                  -->
<!-- This configuration must be used in conjunction with jetty.xml -->
<!-- and jetty-ssl.xml.                                            -->
<!-- ============================================================= -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <!-- =========================================================== -->
  <!-- Add a HTTPS Connector.                                      -->
  <!-- Configure an o.e.j.server.ServerConnector with connection   -->
  <!-- factories for TLS (aka SSL) and HTTP to provide HTTPS.      -->
  <!-- All accepted TLS connections are wired to a HTTP connection.-->
  <!--                                                             -->
  <!-- Consult the javadoc of o.e.j.server.ServerConnector,        -->
  <!-- o.e.j.server.SslConnectionFactory and                       -->
  <!-- o.e.j.server.HttpConnectionFactory for all configuration    -->
  <!-- that may be set here.                                       -->
  <!-- =========================================================== -->
  <Call id="httpsConnector" name="addConnector">
    <Arg>
      <New class="org.eclipse.jetty.server.ServerConnector">
        <Arg name="server"><Ref refid="Server" /></Arg>
          <Arg name="factories">
            <Array type="org.eclipse.jetty.server.ConnectionFactory">
              <Item>
                <New class="org.eclipse.jetty.server.SslConnectionFactory">
                  <Arg name="next">http/1.1</Arg>
                  <Arg name="sslContextFactory"><Ref refid="sslContextFactory"/></Arg>
                </New>
              </Item>
              <Item>
                <New class="org.eclipse.jetty.server.HttpConnectionFactory">
                  <Arg name="config"><Ref refid="sslHttpConfig"/></Arg>
                </New>
              </Item>
            </Array>
          </Arg>
          <Set name="host"><Property name="jetty.host" /></Set>
          <Set name="port"><Property name="https.port" default="8443" /></Set>
          <Set name="idleTimeout"><Property name="https.timeout" default="30000"/></Set>
        </New>
    </Arg>
  </Call>
</Configure>

Now, it should be running, but…

That is all much to complex. I just want a quick fix to get it running!

So, now it is working. But you still have to clutter your project with several files and avoid some pitfalls (belive me or not: if you put the filenames in the <jettyXml>-tag of your pom.xml on separate lines, jetty won’t start!). Last but not least, the HTTP-Connector will stop working, if you forget to add the jetty-http.xml, that is mentioned at the end of the example.

Because of that, I’ve created a simple 6-step quick-fix-guide to get the HTTPS-Connector of the jetty-maven-plugin running.

Quick Fix

  1. Download jetty.xml or copy it from above and place it in src/test/resources/jetty.xml
  2. Download jetty-http.xml or copy it from above and place it in src/test/resources/jetty-http.xml
  3. Download jetty-ssl.xml or copy it from above and place it in src/test/resources/jetty-ssl.xml
  4. Download jetty-https.xml or copy it from above and place it in src/test/resources/jetty-https.xml
  5. Download jetty.keystore or generate it with the command keytool-command from above and place it in src/test/resources/jetty.keystore
  6. Update the configuration of the jetty-maven-plugin in your pom.xml to include the XML-configurationfiles. But be aware, the ordering of the files is important and there should be no newlines inbetween. You have been warned! It should look like:
    
    <plugin>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-maven-plugin</artifactId>
      <configuration>
        <jettyXml>
          ${project.basedir}/src/test/resources/jetty.xml,${project.basedir}/src/test/resources/jetty-http.xml,${project.basedir}/src/test/resources/jetty-ssl.xml,${project.basedir}/src/test/resources/jetty-https.xml
        </jettyXml>
      </configuration>
    </plugin>
      

That’s it. You should be done!

Comments / Questions

  1. Mike Croteau says:

    Do you have a Patreon account? How can I buy you a coffee! This had to have taken a long time to configure. Thank you.

  2. Mike Croteau says:

    I am getting an invalid certificate on the browser window. What could be causing this? Everything seems to be exact.

    Thank you by the way! Im excited to have gotten this far thanks to you.

  3. Maurice Wipf says:

    I had rtouble to access the webapp with https.

    I figured out that there are two locahosts:

    https://localhost:8443 and
    http://localhost:8080.

    The ports are configured in jetty-https.xml and jetty-http.xml.

  4. Phil says:

    Awesome tutorial. Works like magic. Better than the jetty docs.

  5. Simon says:

    Thank you for the very helpful instructions. I am getting close but seeing “Failure: no valid keystore” error for 9.3.3. Any ideas or suggestions?

  6. Richard says:

    Thanks very much ! I just used your 6 files verbatim and it and it all worked fine on Jetty 9.3.12
    You have saved me endless hours resolving this…

    Btw I have put the xml files and keystone in a a folder /jetty and it all works fine, to keep these config files out of application resource folders

  7. Nikola says:

    Thanks a million! This worked like a charm.

  8. Matt Tyson says:

    Wow. I can’t believe how involved that is now. Thanks for writing the only clear description on the internet.

  9. Felipe says:

    Hi, this is awesome, I think the article needs updating, on jetty (9.3.5.v20151012) I had to place your jetty-ssl.xml in a jetty-ssl-context.xml then it worked fine. cheers and thank you!

  10. Goncalo Bv says:

    Great post! Works as a charm.

  11. Jayadev says:

    Awesome Post. Followed the steps and could easily configure the Jetty with SSL. Thanks a lot :)

  12. Mark says:

    Awesome job! Just download the files, copy three lines and presto, instant SSL without having to bash your head against the wall.

    One question. Why do the files have to be in src/test and not src/main? I actually first put them in src/main and changed the jetty-ssl.xml file to have src/main as the default for the KeyStorePath and TrustStorePath properties.

  13. Matteo says:

    So useful.. many thanks!
    Perfectly running with Jetty Maven Plugin 9.2.9.v20150224

    Matteo

  14. romu says:

    life saver , thanks alot

  15. Eric says:

    Thanks for this! Perfect for devs trawling the net for a quick and easy solution to make https work for maven-jetty-plugin 9.x. Used this for 9.1.5.x and worked fine!

Leave a Reply

Your email address will not be published. Required fields are marked *