Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-users] TLS ALPN ACME Lets Encrypt challange

Hi,

a short update, I got everything up and running as you have stated with the default Jetty classes in combination with SPI FLY, the startup level of the bundles did the trick. I only had to override the OpenJDK8ServerALPNProcessor because the implemented Jetty class in my used version does not handle the JDK internal ALPN classes correctly.
Also the correct Jetty XML configuration had me chasing my tail.
I now have the h2 and the acme-tls/1 protocols configured for ALPN and I have registered an ACMEServerConnectionFactory with connection like you suggested:

 ServerConnector@54195e0b{SSL,[ssl]}{0.0.0.0:0} added SslConnectionFactory@1ffc6973{SSL->http/1.1}  ServerConnector@54195e0b{SSL,[ssl, http/1.1]}{0.0.0.0:0} added HttpConnectionFactory@409c700e[HTTP/1.1]
 Using OpenJDK ALPN APIs instead of Jetty ALPN APIs
 ServerConnector@531981d9{SSL,[ssl]}{0.0.0.0:0} added SslConnectionFactory@1f457b{SSL->alpn}  ServerConnector@531981d9{SSL,[ssl, alpn]}{0.0.0.0:0} added ALPNServerConnectionFactory@55f637ed{[alpn],http/1.1,[acme-tls/1, h2]}  ServerConnector@531981d9{SSL,[ssl, alpn, acme-tls/1]}{0.0.0.0:0} added ACMEServerConnectionFactory@6c239faa[acme-tls/1]  ServerConnector@531981d9{SSL,[ssl, alpn, acme-tls/1, h2]}{0.0.0.0:0} added HTTP2ServerConnectionFactory@732bdcbe[h2]  ServerConnector@531981d9{SSL,[ssl, alpn, acme-tls/1, h2, http/1.1]}{0.0.0.0:0} added HttpConnectionFactory@9656b3d[HTTP/1.1]  ServerConnector@6d486ca3{HTTP/1.1,[http/1.1]}{0.0.0.0:0} added HttpConnectionFactory@1d66606f[HTTP/1.1]
 ...
 Pax Web available at [0.0.0.0]:[8443]
 Pax Web available at [0.0.0.0]:[443]
 ...
 Started 0.0.0.0:8443@54195e0b{SSL,[ssl, http/1.1]}{0.0.0.0:8443}
 Started 0.0.0.0:443@531981d9{SSL,[ssl, alpn, acme-tls/1, h2, http/1.1]}{0.0.0.0:443}

If i set sslParameters.setApplicationProtocols("h2") then after the handshake the sslSocket.getApplicationProtocol() returns h2 and establishes a connection:

 onOpened (0+-2) < 1000 SslConnection@5202f44::SocketChannelEndPoint@11fabbf{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=1/30000}{io=0/0,kio=0,kro=0}->SslConnection@5202f44{NOT_HANDSHAKING,eio=-1/-1,di=-1,fill=IDLE,flush=IDLE}~>DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=0/30000}=>ALPNServerConnection@d96b450  onOpened (0+-1) < 1000 ALPNServerConnection@d96b450::DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=1/30000}  apply ALPNServerConnection@d96b450::DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=114/30000} [h2]  onClosed (0+-2) < 1000 ALPNServerConnection@d96b450::DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=120/30000}  onOpened (0+-1) < 1000 HTTP2ServerConnection@18814aae::DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=120/30000}  TLS handshake succeeded, protocol=h2 for ALPNServerConnection@d96b450::DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=22/30000}  onClosed (0+-2) < 1000 HTTP2ServerConnection@18814aae::DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,CLOSED,fill=-,flush=-,to=2/30000}  onClosed (0+-3) < 1000 SslConnection@5202f44::SocketChannelEndPoint@11fabbf{/127.0.0.1:51815<->/127.0.0.1:443,CLOSED,fill=-,flush=-,to=1/30000}{io=1/1,kio=-1,kro=-1}->SslConnection@5202f44{NOT_HANDSHAKING,eio=-1/-1,di=-1,fill=IDLE,flush=IDLE}~>DecryptedEndPoint@4022a184{/127.0.0.1:51815<->/127.0.0.1:443,CLOSED,fill=-,flush=-,to=3/30000}=>HTTP2ServerConnection@18814aae

If i set sslParameters.setApplicationProtocols("acme-tls/1") then the handshake throws connection abort exception because the endpoint is closed by my ACMEServerConnection onOpen();

onOpened (0+0) < 1000 SslConnection@4c64e707::SocketChannelEndPoint@271ff744{/127.0.0.1:51762<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=1/30000}{io=0/0,kio=0,kro=0}->SslConnection@4c64e707{NOT_HANDSHAKING,eio=-1/-1,di=-1,fill=IDLE,flush=IDLE}~>DecryptedEndPoint@4207283d{/127.0.0.1:51762<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=1/30000}=>ALPNServerConnection@6b6a3a62  onOpened (0+1) < 1000 ALPNServerConnection@6b6a3a62::DecryptedEndPoint@4207283d{/127.0.0.1:51762<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=2/30000}  apply ALPNServerConnection@6b6a3a62::DecryptedEndPoint@4207283d{/127.0.0.1:51762<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=11314/30000} [acme-tls/1]  proto=acme-tls/1 tls=TLSv1.2 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 acceptable=true
 Creating ACME server connection
 onClosed (0+0) < 1000 ALPNServerConnection@6b6a3a62::DecryptedEndPoint@4207283d{/127.0.0.1:51762<->/127.0.0.1:443,OPEN,fill=-,flush=-,to=11321/30000}
 Closing endpoint
 onClosed (0+-1) < 1000 ACMEServerConnection@2e88ce94::DecryptedEndPoint@4207283d{/127.0.0.1:51762<->/127.0.0.1:443,CLOSED,fill=-,flush=-,to=11324/30000}  onClosed (0+-2) < 1000 SslConnection@4c64e707::SocketChannelEndPoint@271ff744{/127.0.0.1:51762<->/127.0.0.1:443,CLOSED,fill=-,flush=-,to=2/30000}{io=1/1,kio=-1,kro=-1}->SslConnection@4c64e707{NEED_UNWRAP,eio=-1/-1,di=-1,fill=IDLE,flush=IDLE}~>DecryptedEndPoint@4207283d{/127.0.0.1:51762<->/127.0.0.1:443,CLOSED,fill=-,flush=-,to=11325/30000}=>ACMEServerConnection@2e88ce94

Default HTTPS connections without ALPN interaction work as expected over the same port.

My understanding of the ACME TLS-ALPN-01 using ACME4J is as follows:

A cron job creates a ACME4j session object. A session is used to track the communication with the ACME server (without creating a new account). ACME4J uses an internal HttpURLConnection for communication with a CA provider like Lets Encrypt. Login to your provider account with the location URL and the KeyPair using the ACME4J session object to get your account. Then I can create an order for a new certificate which then contains a Authorization that has to be processed if in PENDING state. This Authorization then contains the challenges from which i will select the TlsAlpn01Challenge . I create a self signed certificate using the byte array from the challenge and configure Jetty 443 port with this (question: how to do that). Then trigger the challenge and let Jetty respond to multiple TLS requests with the ALPN extension acme-tls/1 until the Authorization status is VALID. Question, what response? I guess the generated key pair is the answer the CA is waiting for not needing to complete the handshake? Or does it need to complete the handshake? Then the Order should be able to get the new certificate and I update the JKS with it. Restart Jetty or do a JKS refresh depending on Jetty ability.


On 30/11/2022 19:56, Simone Bordet wrote:
Hi,

On Wed, Nov 30, 2022 at 7:29 PM Lothar Kimmeringer <job@xxxxxxxxxxxxxx> wrote:
The client in question is acme4j and the part between the ACME-
server and that client up to the point where the necessary
data is available for the creation of the certificate is done
there. But then you need to make sure that said certificate
is used by the corresponding ALPN processor which is where
Maurice's question was pointed (I suppose because that's my
question at this point after reading the thread). [1]
Sure. Preparing the certificate with the right extensions is outside
of Jetty's scope.
Let's say you do it manually although I imagine you can do it automatically.
You prepare the certificate into a KeyStore, point Jetty at that
KeyStore and start Jetty with
"jetty.alpn.protocols=acme-tls/1,http/1.1".
This sets up your "TLS server" (as per wording of the RFC) with
support for ACME TLS-ALPN-01 and for HTTP/1.1.

At this point a browser connecting to your TLS server will receive an
"insecure site, self-signed certificate" message, but you can persist
and browse.

If the ACME server (i.e. the CA) contacts your TLS server, it will
open a connection, negotiate the ALPN protocol "acme-tls/1"
successfully, and the TLS server will send the certificate during the
TLS handshake.
The ACME server will receive the certificate and validate it.
Jetty's work is finished.

At this point it is my understanding that the ACME client should
contact the ACME server and download the CA-signed certificate, and
then alert in some way the sysop (outside of Jetty scope).
Once the sysop has the certificate, it will install it in the KeyStore
(or create a new KeyStore and use Jetty's KeyStoreScanner to reload
it), again a manual step (that can probably be automated) that is
outside Jetty's scope.

Your answer "nothing needs to be done" doesn't sound right,
there must be some way to provide that self signed certificate
at a time after startup of the server (and make it unavailable
after successful authentication at the CA) so at some point you
need to get your hand on an existing processor to "set" the
certificate to be used or create an own implementation of such
a processor in order to be able to the same.
See above, the self-signed certificate is automatically provided to
the CA (the ACME server) when it makes a connection with the
"acme-tls/1" ALPN protocol to the TLS server, during the TLS
handshake.
There is nothing that a Jetty user needs to do, apart from setting up
the KeyStore properly and configuring the ALPN protocols: no code to
write, only configuration.



Back to the top