➜ Old React website
Chung Cheuk Hang MichaelJava Web Developer
Spring Security 漏洞 CVE-2023-20860Picocli:開發 Java CLI 工具

客製化 JRE

Table of contents

1 Java module system 簡介

Java 9 為 Java 加入左 module system(亦稱為 Project Jigsaw),佢可以畀我地喺官方既 JDK 裡面揀選我地需要既 modules 去建立我地既客製化 JRE。
對一般既 web apps 黎講,呢個功能未必有咩實際作用。
今時今日,好多公司已經轉曬用 container(Docker 技術)既方式部署 web apps。現時最流行既 JRE Docker images 包括:
  • Docker Hub 上既 openjdk(非 production-ready,並且已經 deprecated)
  • Docker Hub 上既 azul/zulu-openjdk(Azul Zulu OpenJDK)
  • Docker Hub 上既 amazoncorretto(Amazon Corretto OpenJDK)
  • Microsoft 喺 Microsoft Artifact Registry(MCR)上既 Microsoft OpenJDK
除左 Microsoft OpenJDK、部分 Java 版本既 openjdk 之外,呢啲 OpenJDK 既 distributions 應該都有提供 JRE 既 image tags(實際情況要逐個 distribution、逐個 Java 版本咁去 check)。理論上 JRE 版本裡面唔會包含 JDK 既執行檔,例如 javac。否則,如果喺生產環境度部署左基於完整 JDK Docker image 既 Java web apps,就有可能構成安全問題。
如果我地用既 Docker image 冇提供 JRE 既 image tags,又或者因為某啲 hardening 既需要而想客製化一個 JRE 出黎,咁就需要用到 JDK 裡面既 jlink CLI 工具。

2 使用 jlink 客製化 JRE Docker image

2.1 建立 Dockerfile

以下 Dockerfile 既內容參考左 Microsoft 官方教學
1FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu as runtime-build 2 3RUN $JAVA_HOME/bin/jlink \ 4--add-modules \ 5java.base,\ 6<other module 1>,\ 7<other module 2> \ 8--strip-debug \ 9--no-man-pages \ 10--no-header-files \ 11--compress=2 \ 12--output /javaruntime 13 14FROM ubuntu 15ENV LANG C.UTF-8 16ENV JAVA_HOME /usr/lib/jvm/msopenjdk-17-amd64 17ENV PATH "${JAVA_HOME}/bin:${PATH}" 18COPY --from=runtime-build /javaruntime $JAVA_HOME 19 20EXPOSE 8080 21 22RUN mkdir /opt/app 23COPY app.jar /opt/app 24CMD ["java", "-jar", "/opt/app/app.jar"]
註:
  • 呢篇 blog 文既 focus 喺 jlink 果句 command 度。
  • 透過修改 jlink command 既 --add-modules option,我地可以加減 JRE 既 Java modules,從而喺 build time 控制 Docker image 裡面包含既執行檔。
    • --add-modules option 既 value 係我地想個 JRE 需要包含既 Java modules。
      • 有寫明先會包含,否則唔會包含。
      • Java module 名之間以逗號 , 分隔,唔可以有任何空格。
    • Java modules 之間有 dependencies,例如所有 Java modules 都 depend on java.base,所以一個 JRE 至少一定會有 java 以及 keytool 既執行檔。
    • 雖然有啲 Java modules 所需要既執行檔係一樣,但我地係唔可以從一個 JRE 所包含既執行檔去推論返個 JRE 所包含既 Java modules。
      • 包含唔同既 Java modules 會令 JRE 裡面既 lib/modules 檔案內容唔同。
      • 只有透過 java --list-modules 先可以知道個 JRE 裡面實際包含既所有 Java modules。

2.2 建立 Java web app Docker image

  1. 建立一個全新既空 folder。
  2. 將上一步既 Dockerfile 放入個 folder 度。
  3. 將一個 Spring Boot Java web app 既 artifact 放入個 folder 度,名為 app.jar
  4. 執行 docker image build -t myapp .
    • 我地既 Java web app Docker image 既 name 就會係 myapp,而完整 tag 名係 myapp:latest
  5. 執行 docker image list 就會見到個 myapp Docker image。

2.3 執行 Java web app

  1. 執行 docker container run -d --rm --name myapp -p 8080:8080 myapp
  2. 執行 docker container list 就會見到個叫 myapp 既 container 行緊。
  3. 用 cURL 或者網頁瀏覽器就可以測試到個 Java web app,例如 http://localhost:8080/actuator/health

3 Modules、執行檔

3.1 OpenJDK 17

3.1.1 Module 列表

我地可以用 java --list-modules command:
docker container run --rm openjdk:17 java --list-modules docker container run --rm mcr.microsoft.com/openjdk/jdk:17-ubuntu java --list-modules
以下列出 JDK 17 提供既所有 modules(一共有 70 個):
  1. java.base
  2. java.compiler
  3. java.datatransfer
  4. java.desktop
  5. java.instrument
  6. java.logging
  7. java.management
  8. java.management.rmi
  9. java.naming
  10. java.net.http
  11. java.prefs
  12. java.rmi
  13. java.scripting
  14. java.se
  15. java.security.jgss
  16. java.security.sasl
  17. java.smartcardio
  18. java.sql
  19. java.sql.rowset
  20. java.transaction.xa
  21. java.xml
  22. java.xml.crypto
  23. jdk.accessibility
  24. jdk.attach
  25. jdk.charsets
  26. jdk.compiler
  27. jdk.crypto.cryptoki
  28. jdk.crypto.ec
  29. jdk.dynalink
  30. jdk.editpad
  31. jdk.hotspot.agent
  32. jdk.httpserver
  33. jdk.incubator.foreign
  34. jdk.incubator.vector
  35. jdk.internal.ed
  36. jdk.internal.jvmstat
  37. jdk.internal.le
  38. jdk.internal.opt
  39. jdk.internal.vm.ci
  40. jdk.internal.vm.compiler
  41. jdk.internal.vm.compiler.management
  42. jdk.jartool
  43. jdk.javadoc
  44. jdk.jcmd
  45. jdk.jconsole
  46. jdk.jdeps
  47. jdk.jdi
  48. jdk.jdwp.agent
  49. jdk.jfr
  50. jdk.jlink
  51. jdk.jpackage
  52. jdk.jshell
  53. jdk.jsobject
  54. jdk.jstatd
  55. jdk.localedata
  56. jdk.management
  57. jdk.management.agent
  58. jdk.management.jfr
  59. jdk.naming.dns
  60. jdk.naming.rmi
  61. jdk.net
  62. jdk.nio.mapmode
  63. jdk.random
  64. jdk.sctp
  65. jdk.security.auth
  66. jdk.security.jgss
  67. jdk.unsupported
  68. jdk.unsupported.desktop
  69. jdk.xml.dom
  70. jdk.zipfs

3.1.2 執行檔列表

我地可以用 ls <JAVA_HOME>/bin command:
docker container run --rm openjdk:17 ls /usr/java/openjdk-17/bin docker container run --rm mcr.microsoft.com/openjdk/jdk:17-ubuntu ls /usr/lib/jvm/msopenjdk-17-amd64/bin
以下列出 JDK 17 提供既所有執行檔(一共有 28 個):
  1. jar
  2. jarsigner
  3. java
  4. javac
  5. javadoc
  6. javap
  7. jcmd
  8. jconsole
  9. jdb
  10. jdeprscan
  11. jdeps
  12. jfr
  13. jhsdb
  14. jimage
  15. jinfo
  16. jlink
  17. jmap
  18. jmod
  19. jpackage
  20. jps
  21. jrunscript
  22. jshell
  23. jstack
  24. jstat
  25. jstatd
  26. keytool
  27. rmiregistry
  28. serialver

3.1.3 Modules、執行檔之間既關係

Java modules 既多少會影響個 JRE 既 /bin folder 裡面既執行檔。以下列出 JDK 17 提供既所有 modules 以及執行檔之間既關係。

3.1.3.1 從 Java modules 既角度

#Java module執行檔
1java.basejavakeytool
2java.compilerjavakeytool
3java.datatransferjavakeytool
4java.desktopjavakeytool
5java.instrumentjavakeytool
6java.loggingjavakeytool
7java.managementjavakeytool
8java.management.rmijavakeytoolrmiregistry
9java.namingjavakeytool
10java.net.httpjavakeytool
11java.prefsjavakeytool
12java.rmijavakeytoolrmiregistry
13java.scriptingjavajrunscriptkeytool
14java.sejavajrunscriptkeytoolrmiregistry
15java.security.jgssjavakeytool
16java.security.sasljavakeytool
17java.smartcardiojavakeytool
18java.sqljavakeytool
19java.sql.rowsetjavakeytool
20java.transaction.xajavakeytool
21java.xmljavakeytool
22java.xml.cryptojavakeytool
23jdk.accessibilityjavakeytool
24jdk.attachjavakeytool
25jdk.charsetsjavakeytool
26jdk.compilerjavajavackeytoolserialver
27jdk.crypto.cryptokijavakeytool
28jdk.crypto.ecjavakeytool
29jdk.dynalinkjavakeytool
30jdk.editpadjavakeytool
31jdk.hotspot.agentjavajhsdbjrunscriptkeytoolrmiregistry
32jdk.httpserverjavakeytool
33jdk.incubator.foreignjavakeytool
34jdk.incubator.vectorjavakeytool
35jdk.internal.edjavakeytool
36jdk.internal.jvmstatjavakeytool
37jdk.internal.lejavakeytool
38jdk.internal.optjavakeytool
39jdk.internal.vm.cijavakeytool
40jdk.internal.vm.compilerjavakeytool
41jdk.internal.vm.compiler.managementjavakeytool
42jdk.jartooljarjarsignerjavakeytool
43jdk.javadocjavajavacjavadockeytoolserialver
44jdk.jcmdjavajcmdjinfojmapjpsjstackjstatkeytool
45jdk.jconsolejavajconsolekeytoolrmiregistry
46jdk.jdepsjavajavacjavapjdeprscanjdepskeytoolserialver
47jdk.jdijavajdbkeytool
48jdk.jdwp.agentjavakeytool
49jdk.jfrjavajfrkeytool
50jdk.jlinkjavajavacjavapjdeprscanjdepsjimagejlinkjmodkeytoolserialver
51jdk.jpackagejavajavacjavapjdeprscanjdepsjimagejlinkjmodjpackagekeytoolserialver
52jdk.jshelljavajavacjdbjshellkeytoolserialver
53jdk.jsobjectjavakeytool
54jdk.jstatdjavajstatdkeytoolrmiregistry
55jdk.localedatajavakeytool
56jdk.managementjavakeytool
57jdk.management.agentjavakeytoolrmiregistry
58jdk.management.jfrjavajfrkeytool
59jdk.naming.dnsjavakeytool
60jdk.naming.rmijavakeytoolrmiregistry
61jdk.netjavakeytool
62jdk.nio.mapmodejavakeytool
63jdk.randomjavakeytool
64jdk.sctpjavakeytool
65jdk.security.authjavakeytool
66jdk.security.jgssjavakeytool
67jdk.unsupportedjavakeytool
68jdk.unsupported.desktopjavakeytool
69jdk.xml.domjavakeytool
70jdk.zipfsjavakeytool

3.1.3.2 從執行檔既角度

#執行檔Java modules
1jarjdk.jartool
2jarsignerjdk.jartool
3java全部
4javacjdk.compilerjdk.javadocjdk.jdepsjdk.jlinkjdk.jpackagejdk.jshell
5javadocjdk.javadoc
6javapjdk.jdepsjdk.jlinkjdk.jpackage
7jcmdjdk.jcmd
8jconsolejdk.jconsole
9jdbjdk.jdijdk.jshell
10jdeprscanjdk.jdepsjdk.jlinkjdk.jpackage
11jdepsjdk.jdepsjdk.jlinkjdk.jpackage
12jfrjdk.jfrjdk.management.jfr
13jhsdbjdk.hotspot.agent
14jimagejdk.jlinkjdk.jpackage
15jinfojdk.jcmd
16jlinkjdk.jlinkjdk.jpackage
17jmapjdk.jcmd
18jmodjdk.jlinkjdk.jpackage
19jpackagejdk.jpackage
20jpsjdk.jcmd
21jrunscriptjava.scriptingjava.sejdk.hotspot.agent
22jshelljdk.jshell
23jstackjdk.jcmd
24jstatjdk.jcmd
25jstatdjdk.jstatd
26keytool全部
27rmiregistryjava.management.rmijava.rmijava.sejdk.hotspot.agentjdk.jconsolejdk.jstatdjdk.management.agentjdk.naming.rmi
28serialverjdk.compilerjdk.javadocjdk.jdepsjdk.jlinkjdk.jpackagejdk.jshell

3.2 OpenJDK 21

3.2.1 Module 列表

我地可以用 java --list-modules command:
docker container run --rm openjdk:21 java --list-modules docker container run --rm mcr.microsoft.com/openjdk/jdk:21-ubuntu java --list-modules
以下列出 JDK 21 提供既所有 modules(一共有 69 個):
  1. java.base
  2. java.compiler
  3. java.datatransfer
  4. java.desktop
  5. java.instrument
  6. java.logging
  7. java.management
  8. java.management.rmi
  9. java.naming
  10. java.net.http
  11. java.prefs
  12. java.rmi
  13. java.scripting
  14. java.se
  15. java.security.jgss
  16. java.security.sasl
  17. java.smartcardio
  18. java.sql
  19. java.sql.rowset
  20. java.transaction.xa
  21. java.xml
  22. java.xml.crypto
  23. jdk.accessibility
  24. jdk.attach
  25. jdk.charsets
  26. jdk.compiler
  27. jdk.crypto.cryptoki
  28. jdk.crypto.ec
  29. jdk.dynalink
  30. jdk.editpad
  31. jdk.hotspot.agent
  32. jdk.httpserver
  33. jdk.incubator.vector
  34. jdk.internal.ed
  35. jdk.internal.jvmstat
  36. jdk.internal.le
  37. jdk.internal.opt
  38. jdk.internal.vm.ci
  39. jdk.internal.vm.compiler
  40. jdk.internal.vm.compiler.management
  41. jdk.jartool
  42. jdk.javadoc
  43. jdk.jcmd
  44. jdk.jconsole
  45. jdk.jdeps
  46. jdk.jdi
  47. jdk.jdwp.agent
  48. jdk.jfr
  49. jdk.jlink
  50. jdk.jpackage
  51. jdk.jshell
  52. jdk.jsobject
  53. jdk.jstatd
  54. jdk.localedata
  55. jdk.management
  56. jdk.management.agent
  57. jdk.management.jfr
  58. jdk.naming.dns
  59. jdk.naming.rmi
  60. jdk.net
  61. jdk.nio.mapmode
  62. jdk.random
  63. jdk.sctp
  64. jdk.security.auth
  65. jdk.security.jgss
  66. jdk.unsupported
  67. jdk.unsupported.desktop
  68. jdk.xml.dom
  69. jdk.zipfs

3.2.2 執行檔列表

我地可以用 ls <JAVA_HOME>/bin command:
docker container run --rm openjdk:21 ls /usr/java/openjdk-21/bin docker container run --rm mcr.microsoft.com/openjdk/jdk:21-ubuntu ls /usr/lib/jvm/msopenjdk-21-amd64/bin
以下列出 JDK 21 提供既所有執行檔(一共有 29 個):
  1. jar
  2. jarsigner
  3. java
  4. javac
  5. javadoc
  6. javap
  7. jcmd
  8. jconsole
  9. jdb
  10. jdeprscan
  11. jdeps
  12. jfr
  13. jhsdb
  14. jimage
  15. jinfo
  16. jlink
  17. jmap
  18. jmod
  19. jpackage
  20. jps
  21. jrunscript
  22. jshell
  23. jstack
  24. jstat
  25. jstatd
  26. jwebserver
  27. keytool
  28. rmiregistry
  29. serialver

3.2.3 Modules、執行檔之間既關係

Java modules 既多少會影響個 JRE 既 /bin folder 裡面既執行檔。以下列出 JDK 21 提供既所有 modules 以及執行檔之間既關係。

3.2.3.1 從 Java modules 既角度

#Java module執行檔
1java.basejavakeytool
2java.compilerjavakeytool
3java.datatransferjavakeytool
4java.desktopjavakeytool
5java.instrumentjavakeytool
6java.loggingjavakeytool
7java.managementjavakeytool
8java.management.rmijavakeytoolrmiregistry
9java.namingjavakeytool
10java.net.httpjavakeytool
11java.prefsjavakeytool
12java.rmijavakeytoolrmiregistry
13java.scriptingjavajrunscriptkeytool
14java.sejavajrunscriptkeytoolrmiregistry
15java.security.jgssjavakeytool
16java.security.sasljavakeytool
17java.smartcardiojavakeytool
18java.sqljavakeytool
19java.sql.rowsetjavakeytool
20java.transaction.xajavakeytool
21java.xmljavakeytool
22java.xml.cryptojavakeytool
23jdk.accessibilityjavakeytool
24jdk.attachjavakeytool
25jdk.charsetsjavakeytool
26jdk.compilerjavajavackeytoolserialver
27jdk.crypto.cryptokijavakeytool
28jdk.crypto.ecjavakeytool
29jdk.dynalinkjavakeytool
30jdk.editpadjavakeytool
31jdk.hotspot.agentjavajhsdbjrunscriptkeytoolrmiregistry
32jdk.httpserverjavajwebserverkeytool
33jdk.incubator.vectorjavakeytool
34jdk.internal.edjavakeytool
35jdk.internal.jvmstatjavakeytool
36jdk.internal.lejavakeytool
37jdk.internal.optjavakeytool
38jdk.internal.vm.cijavakeytool
39jdk.internal.vm.compilerjavakeytool
40jdk.internal.vm.compiler.managementjavakeytool
41jdk.jartooljarjarsignerjavakeytool
42jdk.javadocjavajavacjavadockeytoolserialver
43jdk.jcmdjavajcmdjinfojmapjpsjstackjstatkeytool
44jdk.jconsolejavajconsolekeytoolrmiregistry
45jdk.jdepsjavajavacjavapjdeprscanjdepskeytoolserialver
46jdk.jdijavajdbkeytool
47jdk.jdwp.agentjavakeytool
48jdk.jfrjavajfrkeytool
49jdk.jlinkjavajavacjavapjdeprscanjdepsjimagejlinkjmodkeytoolserialver
50jdk.jpackagejavajavacjavapjdeprscanjdepsjimagejlinkjmodjpackagekeytoolserialver
51jdk.jshelljavajavacjdbjshellkeytoolserialver
52jdk.jsobjectjavakeytool
53jdk.jstatdjavajstatdkeytoolrmiregistry
54jdk.localedatajavakeytool
55jdk.managementjavakeytool
56jdk.management.agentjavakeytoolrmiregistry
57jdk.management.jfrjavajfrkeytool
58jdk.naming.dnsjavakeytool
59jdk.naming.rmijavakeytoolrmiregistry
60jdk.netjavakeytool
61jdk.nio.mapmodejavakeytool
62jdk.randomjavakeytool
63jdk.sctpjavakeytool
64jdk.security.authjavakeytool
65jdk.security.jgssjavakeytool
66jdk.unsupportedjavakeytool
67jdk.unsupported.desktopjavakeytool
68jdk.xml.domjavakeytool
69jdk.zipfsjavakeytool

3.2.3.2 從執行檔既角度

#執行檔Java modules
1jarjdk.jartool
2jarsignerjdk.jartool
3java全部
4javacjdk.compilerjdk.javadocjdk.jdepsjdk.jlinkjdk.jpackagejdk.jshell
5javadocjdk.javadoc
6javapjdk.jdepsjdk.jlinkjdk.jpackage
7jcmdjdk.jcmd
8jconsolejdk.jconsole
9jdbjdk.jdijdk.jshell
10jdeprscanjdk.jdepsjdk.jlinkjdk.jpackage
11jdepsjdk.jdepsjdk.jlinkjdk.jpackage
12jfrjdk.jfrjdk.management.jfr
13jhsdbjdk.hotspot.agent
14jimagejdk.jlinkjdk.jpackage
15jinfojdk.jcmd
16jlinkjdk.jlinkjdk.jpackage
17jmapjdk.jcmd
18jmodjdk.jlinkjdk.jpackage
19jpackagejdk.jpackage
20jpsjdk.jcmd
21jrunscriptjava.scriptingjava.sejdk.hotspot.agent
22jshelljdk.jshell
23jstackjdk.jcmd
24jstatjdk.jcmd
25jstatdjdk.jstatd
26jwebserverjdk.httpserver
27keytool全部
28rmiregistryjava.management.rmijava.rmijava.sejdk.hotspot.agentjdk.jconsolejdk.jstatdjdk.management.agentjdk.naming.rmi
29serialverjdk.compilerjdk.javadocjdk.jdepsjdk.jlinkjdk.jpackagejdk.jshell

4 參考資料