11# ## Builder
22FROM ghcr.io/graalvm/native-image:22.3.3 AS build
33
4- # --- SSL Root CA Refresh (Sectigo R46) ---
4+ # --- SSL Root CA Import (Sectigo Public Server Authentication Root R46) ---
55# Canada Post rotated its certificate chain to a root (Sectigo Public Server Authentication Root R46)
66# missing from the older JDK bundled with this GraalVM image. We need that root in the trust store
77# BEFORE native-image build so the embedded trust store trusts Canada Post endpoints.
8- # Policy: do NOT commit the PEM. Approaches supported:
9- # 1. Provide the PEM via build arg SECTIGO_R46_PEM (recommended; store content in GitHub/OpenShift secret).
10- # 2. Fallback: attempt network fetch from crt.sh (public CT log) if build arg not supplied.
11- ARG SECTIGO_R46_PEM=""
8+ # Source of truth: the reviewed, checked-in public certificate below (see backend/certs/ for
9+ # provenance and rotation notes). This is public CA material, not a secret. The fingerprint is
10+ # pinned and verified before import; there is no network fetch (e.g. crt.sh) in this build path.
11+ ARG SECTIGO_R46_ALIAS="sectigo-r46-root"
12+ ARG SECTIGO_R46_SHA256="7B:B6:47:A6:2A:EE:AC:88:BF:25:7A:A5:22:D0:1F:FE:A3:95:E0:AB:45:C7:3F:93:F6:56:54:EC:38:F2:5A:06"
1213
13- RUN set -e; \
14+ COPY certs/sectigo-r46-root.pem /tmp/sectigo-r46-root.pem
15+
16+ RUN set -eu; \
1417 CERT_FILE=/tmp/sectigo-r46-root.pem; \
15- echo "--- Starting Sectigo CA Import ---" ; \
16- if [ -n "$SECTIGO_R46_PEM" ]; then \
17- echo "Using provided SECTIGO_R46_PEM build argument." ; \
18- printf '%b' "$SECTIGO_R46_PEM" > "$CERT_FILE" ; \
19- else \
20- echo "Attempting remote fetch of Sectigo R46 root..." ; \
21- curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \
22- --retry 5 \
23- --retry-delay 5 \
24- --retry-connrefused \
25- -fsSL 'https://crt.sh/?d=4256644734' \
26- -o "$CERT_FILE" \
27- || { echo "ERROR: Curl failed to connect to crt.sh." ; exit 1; }; \
28- \
29- echo "Verifying downloaded file format..." ; \
30- grep -q "BEGIN CERTIFICATE" "$CERT_FILE" || { \
31- echo "ERROR: Downloaded file is not a valid PEM. crt.sh likely returned an HTML error page. File content:" ; \
32- cat "$CERT_FILE" ; \
33- exit 1; \
34- }; \
18+ ALIAS="$SECTIGO_R46_ALIAS" ; \
19+ KEYSTORE="$JAVA_HOME/lib/security/cacerts" ; \
20+ STOREPASS=changeit; \
21+ echo "--- Sectigo R46 CA import ---" ; \
22+ [ -s "$CERT_FILE" ] || { echo "ERROR: $CERT_FILE is missing or empty. Ensure backend/certs/sectigo-r46-root.pem is committed and included in the build context." ; exit 1; }; \
23+ grep -q "BEGIN CERTIFICATE" "$CERT_FILE" || { echo "ERROR: $CERT_FILE does not contain PEM certificate data." ; exit 1; }; \
24+ [ -f "$KEYSTORE" ] || { echo "ERROR: keystore $KEYSTORE was not found in this image." ; exit 1; }; \
25+ [ -w "$KEYSTORE" ] || { echo "ERROR: keystore $KEYSTORE is not writable." ; exit 1; }; \
26+ echo "Validating certificate with keytool..." ; \
27+ keytool -printcert -file "$CERT_FILE" > /tmp/sectigo-r46-root.info 2>/tmp/sectigo-r46-root.err \
28+ || { echo "ERROR: certificate is malformed and failed to parse:" ; cat /tmp/sectigo-r46-root.err; rm -f "$CERT_FILE" /tmp/sectigo-r46-root.info /tmp/sectigo-r46-root.err; exit 1; }; \
29+ ACTUAL_SHA256=$(grep -i "SHA256:" /tmp/sectigo-r46-root.info | head -1 | sed 's/.*SHA256: *//' | tr -d ' \t ' ); \
30+ [ -n "$ACTUAL_SHA256" ] || { echo "ERROR: could not extract a SHA-256 fingerprint from the certificate." ; exit 1; }; \
31+ EXPECTED_NORM=$(echo "$SECTIGO_R46_SHA256" | tr '[:lower:]' '[:upper:]' | tr -d ':' ); \
32+ ACTUAL_NORM=$(echo "$ACTUAL_SHA256" | tr '[:lower:]' '[:upper:]' | tr -d ':' ); \
33+ if [ "$ACTUAL_NORM" != "$EXPECTED_NORM" ]; then \
34+ echo "ERROR: certificate fingerprint mismatch." ; \
35+ echo " expected sha256: $SECTIGO_R46_SHA256" ; \
36+ echo " actual sha256: $ACTUAL_SHA256" ; \
37+ rm -f "$CERT_FILE" /tmp/sectigo-r46-root.info /tmp/sectigo-r46-root.err; \
38+ exit 1; \
3539 fi; \
36- \
37- echo "Checking if alias already exists in trust store..." ; \
38- if keytool -list -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit -alias sectigo-r46-root >/dev/null 2>&1; then \
39- echo "Alias sectigo-r46-root already exists, skipping import." ; \
40+ echo "Fingerprint verified (sha256=$ACTUAL_SHA256)." ; \
41+ if keytool -list -keystore "$KEYSTORE" -storepass "$STOREPASS" -alias "$ALIAS" >/dev/null 2>&1; then \
42+ EXISTING_SHA256=$(keytool -list -v -keystore "$KEYSTORE" -storepass "$STOREPASS" -alias "$ALIAS" 2>/dev/null \
43+ | grep -i "SHA256:" | head -1 | sed 's/.*SHA256: *//' | tr -d ' \t ' | tr '[:lower:]' '[:upper:]' | tr -d ':' ); \
44+ if [ "$EXISTING_SHA256" = "$EXPECTED_NORM" ]; then \
45+ echo "Alias $ALIAS already present in $KEYSTORE with matching fingerprint; skipping import." ; \
46+ else \
47+ echo "ERROR: alias $ALIAS already exists in $KEYSTORE with a mismatched fingerprint (found $EXISTING_SHA256)." ; \
48+ rm -f "$CERT_FILE" /tmp/sectigo-r46-root.info /tmp/sectigo-r46-root.err; \
49+ exit 1; \
50+ fi; \
4051 else \
41- echo "Importing certificate..." ; \
52+ echo "Importing certificate as alias $ALIAS ..." ; \
4253 keytool -importcert \
4354 -trustcacerts \
4455 -noprompt \
45- -alias sectigo-r46-root \
56+ -alias "$ALIAS" \
4657 -file "$CERT_FILE" \
47- -keystore "$JAVA_HOME/lib/security/cacerts " \
48- -storepass changeit ; \
58+ -keystore "$KEYSTORE " \
59+ -storepass "$STOREPASS" ; \
4960 fi; \
50- \
5161 echo "Verifying successful installation..." ; \
52- keytool -list \
53- -keystore "$JAVA_HOME/lib/security/cacerts" \
54- -storepass changeit \
55- -alias sectigo-r46-root >/dev/null; \
62+ keytool -list -keystore "$KEYSTORE" -storepass "$STOREPASS" -alias "$ALIAS" >/dev/null 2>&1 \
63+ || { echo "ERROR: post-import verification failed for alias $ALIAS." ; exit 1; }; \
5664 \
57- rm -f "$CERT_FILE" ; \
58- echo "--- Sectigo CA Import Successful ---"
65+ rm -f "$CERT_FILE" /tmp/sectigo-r46-root.info /tmp/sectigo-r46-root.err ; \
66+ echo "--- Sectigo R46 CA import complete (alias=$ALIAS, sha256=$ACTUAL_SHA256) ---"
5967
6068# Copy
6169WORKDIR /app
@@ -81,4 +89,4 @@ HEALTHCHECK CMD curl -f http://localhost:8080/actuator/health | grep '"status":"
8189ENV SPRING_PROFILES_ACTIVE=container
8290
8391# Startup
84- ENTRYPOINT ["/app/nr-forest-client-backend" ]
92+ ENTRYPOINT ["/app/nr-forest-client-backend" ]
0 commit comments