letsencrypt-scala: Let's Encrypt for Scala

Motivation

letsencrypt-scala enables effortless SSL for your Scala servers using Let's Encrypt.

When trying to set up a simple server for accepting Heroku's Syslogs over TCP+TLS in real-time for Scala Algorithms, I could not fathom just how complicated it was to set up a simple KeyStore from Let's Encrypt's certificates. Does everyone go through the pain or is this perhaps why SSL termination with nginx / k8s is so popular?.

In any case, I couldn't see myself daring to update KeyStores manually every 90 days, nor could I see myself maintaining custom SSL configurations inside the many different apps that I have, so I realised this would make a great project that would benefit others massively in the Scala/Typelevel world.

With this, we will be able not to just do rapid development with http4s, but also rapid deployment where nginx is no longer needed. Simply expose your app directly onto the network, or put it behind a load balancer with SSL passthrough.  Now you get end-to-end powers where you have a lot more power and customisability, especially from security and HTTP/2 perspectives.

Integrations

I have written 3 documents for you to use this library. As I went through the pain of making it all work, I thought it's not much more to share exactly how to use it :-) - in this case you can skip the rest of this page, and go straight to the links below:

Set-up in SBT

This library is still in its testing phase. Last updated 2021-04-03. Published for Scala 2.13.5, 3.0.0-RC1, 3.0.0-RC3

ThisBuild / resolvers += Resolver.sonatypeRepo("snapshots")
// Comment out as necessary
// cats-effect 2.x
libraryDependencies += "com.scalawilliam" %% "letsencrypt-ce2" % "0.0.5-SNAPSHOT"
// cats-effect 3.x
libraryDependencies += "com.scalawilliam" %% "letsencrypt-ce3" % "0.0.5-SNAPSHOT"

Usage

The default and recommended usage is as follows, although you can pass the target directory explicitly as well. This will read the certificate directory from the environment variable LETSENCRYPT_CERT_DIR or system property letsencrypt.cert.dir.

LetsEncryptScala
  .fromEnvironment[IO]
  .flatMap(sslContext => /* Use with fs2/http4s/other servers */ )

How it works

It parses the Let's Encrypt certbot certificates based on the convention, and then sets up a KeyStore, which then is used to generate an SSLContext - all in the context of a cats-effect 'Resource', to ensure that we fully erase the private key from memory once we're done using it.

Let's encrypt Play!

This sub-library will simplify running Play framework apps with SSL and Let's Encrypt.

Refer to the Play docs: ConfiguringHttps (Play 2.8.x). Rather than dealing with the complex configurations and renewals (every 90 days) of Java KeyStores, just specify where your certificates are and use them directly.

Find the minimal example project here: letsencrypt-scala/play-example.

Dependency:

ThisBuild / resolvers += Resolver.sonatypeRepo("snapshots")
// Compatible with Play 2.8.x - this one is if you prefer to use cats-effect 2
libraryDependencies += "com.scalawilliam" %% "letsencrypt-play_ce2" % "0.0.5-SNAPSHOT"
libraryDependencies += "com.scalawilliam" %% "letsencrypt-play_ce3" % "0.0.5-SNAPSHOT"

Configuration

See Let's Encrypt http4s to double-check how to set up the certificate access permissions first.

Setting the engine provider via property

-Dplay.server.https.engineProvider=com.scalawilliam.letsencrypt.play.LetsEncryptPlay

Setting the engine provider via application.conf

play.server.https.engineProvider = "com.scalawilliam.letsencrypt.play.LetsEncryptPlay"

Using it statically

Refer to the following class:

com.scalawilliam.letsencrypt.play.LetsEncryptPlay

Setting the Certificate directory via environment variable

export LETSENCRYPT_CERT_DIR=/path/to/certificate.com/

Setting the Certificate directory via environment variable

-Dletsencrypt.cert.dir=/path/to/certificate.com/

Disclaimer

This snapshot software is provided without warranty and is not audited for security issues. You may want to consult a Java security expert to verify before using this in critical applications.

Contributions and suggestions -» via GitHub.

Once I am satisfied with the testing, I will release it to Maven Central.