Spring Boot 3.2.0 中的 SSL 熱重載

工程 | Moritz Halbritter | 2023 年 11 月 07 日 | ...

在 Spring Boot 3.2.0 中,我們新增了讓嵌入式 Web 伺服器熱重載 SSL 憑證和金鑰的功能。這表示您可以在不重新啟動應用程式的情況下輪換 SSL 信任資料。Tomcat 和 Netty 嵌入式 Web 伺服器都支援熱重載。

讓我們看看實際運作情況!

首先,我們將使用 OpenSSL 建立我們的 SSL 私密金鑰和匹配的憑證

mkdir certs
cd certs
openssl req -x509 -subj "/CN=demo-cert-1" -keyout demo.key -out demo.crt -sha256 -days 365 -nodes -newkey rsa 

這會在 certs/demo.key 中建立一個私密金鑰,並在 certs/demo.crt 中建立一個具有通用名稱 "demo-cert-1" 的匹配(自我簽署)憑證。

現在,我們使用我們最喜歡的網站 start.spring.io,使用 "Spring Web" 依賴項(預設使用 Tomcat Web 伺服器)建立一個新的 Spring Boot 3.2.0 應用程式。

在應用程式配置中,我們新增以下內容

spring.ssl.bundle.pem:
  demo:
    reload-on-update: true
    keystore:    
      certificate: "certs/demo.crt"
      private-key: "certs/demo.key"

這使用名稱 "demo" 和我們產生的憑證和私密金鑰配置一個 SSL bundlereload-on-update: true 配置指示 Spring Boot 在背景監看這些檔案,如果它們發生變更,則觸發重新載入。

現在我們配置 Web 伺服器以使用該 bundle 並接受連接埠 8443 上的連線

server.ssl.bundle: "demo"
server.port: 8443

讓我們也新增一個簡單的控制器,以純文字 "Hello World" 回應

@RestController
class DemoController {
    @GetMapping(path = "/", produces = MediaType.TEXT_PLAIN_VALUE)
    String helloWorld() {
        return "Hello World";
    }
}

當我們啟動應用程式時,我們在日誌中看到類似這樣的訊息,這確認它已在使用 HTTPS 的 8443 連接埠上啟動。

INFO 82407 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8443 (https)

現在我們可以使用 curl 進行我們的第一個請求

$ curl --insecure https://127.0.0.1:8443/
Hello World%

萬歲,它有效! 我們必須傳遞 --insecure,因為 SSL 憑證是自我簽署的,並且不受 curl 信任。

讓我們將 curl 切換到 verbose 模式,以取得有關發生的 SSL 握手的一些資訊

$ curl --verbose --insecure https://127.0.0.1:8443/
*   Trying 127.0.0.1:8443...
* Connected to localhost (127.0.0.1) port 8443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=demo-cert-1
*  start date: Nov  2 09:22:53 2023 GMT
*  expire date: Nov  1 09:22:53 2024 GMT
*  issuer: CN=demo-cert-1
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8443
> User-Agent: curl/8.0.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 11
< Date: Thu, 02 Nov 2023 09:33:37 GMT
< 
* Connection #0 to host localhost left intact
Hello World

subject: CN=demo-cert-1 行中,您可以看到憑證的通用名稱是 "demo-cert-1",這在稍後將很重要。

讓我們透過使用 OpenSSL 產生新的私密金鑰和憑證,覆寫舊檔案來嘗試熱重載

cd certs
openssl req -x509 -subj "/CN=demo-cert-2" -keyout demo.key -out demo.crt -sha256 -days 365 -nodes -newkey rsa

這次,我們的憑證具有通用名稱 "demo-cert-2"。

過一段時間後,您會在日誌中看到類似這樣的訊息

INFO 83162 --- [-bundle-watcher] o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/home/xxx/.keystore] using alias [tomcat] with trust store [null]

這是說 Tomcat 重新載入了私密金鑰和憑證的複雜方式。

我們現在可以使用 curl 驗證是否使用了新憑證

$ curl --verbose --insecure https://127.0.0.1:8443/
*   Trying 127.0.0.1:8443...
* Connected to localhost (127.0.0.1) port 8443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=demo-cert-2
*  start date: Nov  2 09:37:46 2023 GMT
*  expire date: Nov  1 09:37:46 2024 GMT
*  issuer: CN=demo-cert-2
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8443
> User-Agent: curl/8.0.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 11
< Date: Thu, 02 Nov 2023 09:39:47 GMT
< 
* Connection #0 to host localhost left intact
Hello World

subject: CN=demo-cert-2 行驗證了使用了新憑證。萬歲,大成功!

總結:我們建立了一個 SSL 私密金鑰和憑證,然後我們配置 Spring Boot 以使用它並監看變更。當私密金鑰和憑證變更時,Spring Boot 會重新載入它們,並且它們將在不重新啟動應用程式的情況下被使用。 這不是很酷嗎?!

順便說一句,如果您想知道已經存在的連線會發生什麼事:它們將繼續使用舊憑證,但所有新連線將使用新憑證。

我們希望此功能可以簡化您的操作。 您可以透過測試 Spring Boot 3.2.0-RC2 來嘗試一下。如果您有增強功能的想法或發現錯誤,請不要猶豫並在 我們的追蹤器 上開啟一個 issue。

取得 Spring 電子報

透過 Spring 電子報保持聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部