From 4f1c251ff831a3c1c96348906ee81193dbf5f67b Mon Sep 17 00:00:00 2001 From: sttlab Date: Sun, 3 May 2026 12:48:42 +0000 Subject: [PATCH] Simplification of TLS config --- README.md | 47 ++++++++++++++++++++++++-------- apim-values.yml | 47 +++++++------------------------- deploy.sh | 69 +++++++++++++++++++++++++++++------------------ secrets-create.sh | 9 ++++++- 4 files changed, 97 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 76f95d2..b58138b 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,13 @@ gravitee-selfsigned-bootstrap (Issuer, self-signed) ### cert-manager keystores -Two secrets include PKCS12 keystores automatically generated by cert-manager: -- `api-internal-tls` → `keystore.p12` (used by Jetty) -- `gateway-internal-tls` → `keystore.p12` (used by Vert.x) -- `elasticsearch-tls` → `truststore.jks` (used by the JVM to trust the ES certificate) +Three secrets include PKCS12 keystores automatically generated by cert-manager: +- `api-internal-tls` → `keystore.p12` (Jetty server cert) + `truststore.p12` (CA trust for JVM) +- `gateway-internal-tls` → `keystore.p12` (Vert.x server cert) + `truststore.p12` (CA trust for JVM) +- `elasticsearch-tls` → `keystore.p12` + `truststore.p12` (ES server cert — keystore unused by Gravitee) + +Each component mounts only its own internal TLS secret at `/run/secrets/tls/`, providing both +the server keystore and the JVM truststore from a single volume. Keystore password is stored in the `gravitee-jks-password` secret. @@ -86,7 +89,9 @@ All secrets are created by `secrets-create.sh` before the first deployment. | `gravitee-admin` | `admin-password-plain`, `admin-password-bcrypt` | api (env) | | `gravitee-es-master-credentials` | ES `username`, `password` | api, gateway (env) | | `gravitee-jks-password` | keystore password | api, gateway (env) + cert-manager | -| `gravitee-ca-trust` | `ca.crt` only — no tls.crt/key | nginx ingress proxy-ssl-secret | +| `gravitee-ca-trust` | `ca.crt` only — no tls.crt/key | nginx ingress `proxy-ssl-secret` | + +> `gravitee-ca-trust` is created by `secrets-create.sh` after the certificates are ready (it reads `ca.crt` from `gravitee-ca-tls`). It must contain **only** `ca.crt` — if `tls.crt`/`tls.key` were present, nginx would present them as a client certificate, triggering unintended mTLS toward backends. > `gravitee-ca-trust` is a dedicated secret containing only `ca.crt`. The `proxy-ssl-secret` nginx annotation presents `tls.crt`/`tls.key` as a client certificate if they exist, which would unintentionally trigger mTLS toward backends. @@ -116,10 +121,11 @@ env: TLS enabled via `tls=true` in the connection URI. Server certificate validation is handled by the JVM truststore: ``` -JAVA_OPTS: -Djavax.net.ssl.trustStore=/run/secrets/truststore/truststore.jks +JAVA_OPTS: -Djavax.net.ssl.trustStoreType=PKCS12 + -Djavax.net.ssl.trustStore=/run/secrets/tls/truststore.p12 -Djavax.net.ssl.trustStorePassword=${JKS_PASSWORD} ``` -The `truststore.jks` is sourced from the `elasticsearch-tls` secret (contains the Gravitee CA). +The `truststore.p12` is sourced from the component's own internal TLS secret (`api-internal-tls` or `gateway-internal-tls`), which cert-manager populates with the issuing CA chain. MongoDB runs with `--tlsAllowConnectionsWithoutCertificates` (no client mTLS) but requires server TLS (`--tlsMode=requireTLS`). @@ -173,17 +179,34 @@ annotations: ### First install ```bash -# 1. Create secrets +./deploy.sh +``` + +Or step by step: + +```bash +# 1. Create credential secrets ./secrets-create.sh -# 2. Deploy PKI +# 2. Deploy PKI and wait for certificates kubectl apply -f certificates.yml +kubectl -n gravitee-apim wait --for=condition=Ready certificate --all --timeout=180s -# 3. Deploy backends +# 3. Create CA trust secret for nginx (requires certificates to be ready first) +kubectl -n gravitee-apim get secret gravitee-ca-tls -o jsonpath='{.data.ca\.crt}' | base64 -d | \ + kubectl -n gravitee-apim create secret generic gravitee-ca-trust --from-file=ca.crt=/dev/stdin \ + --dry-run=client -o yaml | kubectl apply -f - + +# 4. Enable nginx ingress snippets +kubectl patch configmap ingress-nginx-controller -n ingress-nginx \ + --type merge -p '{"data":{"allow-snippet-annotations":"true","annotations-risk-level":"Critical"}}' +kubectl rollout restart deployment/ingress-nginx-controller -n ingress-nginx + +# 5. Deploy backends helm upgrade --install mongodb bitnami/mongodb -n gravitee-apim -f mongo-values.yml helm upgrade --install elasticsearch elastic/elasticsearch -n gravitee-apim -f elastic-values.yml -# 4. Deploy Gravitee +# 6. Deploy Gravitee helm upgrade --install graviteeio-apim graviteeio/apim -n gravitee-apim -f apim-values.yml ``` @@ -198,6 +221,8 @@ helm upgrade --install elasticsearch elastic/elasticsearch -n gravitee-apim -f e helm upgrade --install graviteeio-apim graviteeio/apim -n gravitee-apim -f apim-values.yml ``` +> Secrets, certs, PVs and the `gravitee-ca-trust` secret are preserved across workload rebuilds. + ### /etc/hosts ``` diff --git a/apim-values.yml b/apim-values.yml index a7818be..b833505 100644 --- a/apim-values.yml +++ b/apim-values.yml @@ -14,33 +14,19 @@ gateway: enabled: true replicaCount: 2 - # Mount CA bundle and internal server cert extraVolumes: | - - name: gravitee-ca - secret: - secretName: gravitee-ca-tls - items: - - key: ca.crt - path: ca.crt - name: gateway-internal-tls secret: secretName: gateway-internal-tls - - name: es-truststore - secret: - secretName: elasticsearch-tls items: - - key: truststore.jks - path: truststore.jks + - key: keystore.p12 + path: keystore.p12 + - key: truststore.p12 + path: truststore.p12 extraVolumeMounts: | - - name: gravitee-ca - mountPath: /run/secrets/ca - readOnly: true - name: gateway-internal-tls mountPath: /run/secrets/tls readOnly: true - - name: es-truststore - mountPath: /run/secrets/truststore - readOnly: true env: - name: GRAVITEE_MANAGEMENT_MONGODB_URI @@ -73,7 +59,7 @@ gateway: name: gravitee-jks-password key: password - name: JAVA_OPTS - value: "-Djavax.net.ssl.trustStore=/run/secrets/truststore/truststore.jks -Djavax.net.ssl.trustStorePassword=$(JKS_PASSWORD)" + value: "-Djavax.net.ssl.trustStoreType=PKCS12 -Djavax.net.ssl.trustStore=/run/secrets/tls/truststore.p12 -Djavax.net.ssl.trustStorePassword=$(JKS_PASSWORD)" # Enable HTTPS on the gateway listener (port 8082) ssl: @@ -125,31 +111,18 @@ api: replicaCount: 1 extraVolumes: | - - name: gravitee-ca - secret: - secretName: gravitee-ca-tls - items: - - key: ca.crt - path: ca.crt - name: api-internal-tls secret: secretName: api-internal-tls - - name: es-truststore - secret: - secretName: elasticsearch-tls items: - - key: truststore.jks - path: truststore.jks + - key: keystore.p12 + path: keystore.p12 + - key: truststore.p12 + path: truststore.p12 extraVolumeMounts: | - - name: gravitee-ca - mountPath: /run/secrets/ca - readOnly: true - name: api-internal-tls mountPath: /run/secrets/tls readOnly: true - - name: es-truststore - mountPath: /run/secrets/truststore - readOnly: true env: - name: GRAVITEE_MANAGEMENT_MONGODB_URI @@ -192,7 +165,7 @@ api: name: gravitee-jks-password key: password - name: JAVA_OPTS - value: "-Djavax.net.ssl.trustStore=/run/secrets/truststore/truststore.jks -Djavax.net.ssl.trustStorePassword=$(JKS_PASSWORD)" + value: "-Djavax.net.ssl.trustStoreType=PKCS12 -Djavax.net.ssl.trustStore=/run/secrets/tls/truststore.p12 -Djavax.net.ssl.trustStorePassword=$(JKS_PASSWORD)" # Enable HTTPS on Management API + Portal API listeners http: diff --git a/deploy.sh b/deploy.sh index 5381568..95479c4 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash -# Deploy Gravitee APIM OSS on a single-node k3s cluster +# Deploy Gravitee APIM OSS on a single-node k3s cluster. # - Domain: *.gravitee.sttlab.pc # - Ingress: nginx # - TLS: cert-manager with namespace-scoped self-signed CA Issuer -# - Secrets: pre-created (run create-secrets.sh first) +# - Secrets: pre-created by secrets-create.sh set -euo pipefail @@ -12,7 +12,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" echo "==> Checking prerequisites" command -v kubectl >/dev/null || { echo "kubectl not found"; exit 1; } -command -v helm >/dev/null || { echo "helm not found"; exit 1; } +command -v helm >/dev/null || { echo "helm not found"; exit 1; } +command -v htpasswd >/dev/null || { echo "htpasswd not found (required for admin BCrypt hash)"; exit 1; } echo "==> Verifying cluster reachable" kubectl cluster-info @@ -28,14 +29,14 @@ kubectl get ingressclass nginx >/dev/null 2>&1 || { echo "WARNING: 'nginx' IngressClass not found. Ensure nginx-ingress is installed." } -echo "==> Step 1/5 : Create namespace" +echo "==> Step 1/6 : Create namespace" kubectl create namespace "${NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f - -echo "==> Step 2/5 : Create credential secrets (idempotent)" -"${SCRIPT_DIR}/create-secrets.sh" +echo "==> Step 2/6 : Create credential secrets (idempotent)" +"${SCRIPT_DIR}/secrets-create.sh" -echo "==> Step 3/5 : Apply cert-manager Issuer + Certificates" -kubectl apply -f "${SCRIPT_DIR}/manifests/cert-manager.yaml" +echo "==> Step 3/6 : Apply cert-manager Issuers + Certificates" +kubectl apply -f "${SCRIPT_DIR}/certificates.yml" echo "==> Waiting for CA Issuer to be Ready" kubectl -n "${NAMESPACE}" wait --for=condition=Ready issuer/gravitee-ca-issuer --timeout=120s @@ -47,28 +48,41 @@ for cert in gravitee-ca console-tls portal-tls api-tls gateway-tls \ kubectl -n "${NAMESPACE}" wait --for=condition=Ready "certificate/${cert}" --timeout=180s done -echo "==> Step 4/5 : Add Helm repos" -helm repo add bitnami https://charts.bitnami.com/bitnami -helm repo add elastic https://helm.elastic.co -helm repo add graviteeio https://helm.gravitee.io +echo "==> Creating gravitee-ca-trust secret (CA only, for nginx proxy-ssl-secret)" +kubectl -n "${NAMESPACE}" get secret gravitee-ca-tls -o jsonpath='{.data.ca\.crt}' | base64 -d | \ + kubectl -n "${NAMESPACE}" create secret generic gravitee-ca-trust \ + --from-file=ca.crt=/dev/stdin \ + --dry-run=client -o yaml | kubectl apply -f - + +echo "==> Step 4/6 : Configure nginx ingress controller (enable snippets)" +kubectl patch configmap ingress-nginx-controller -n ingress-nginx \ + --type merge \ + -p '{"data":{"allow-snippet-annotations":"true","annotations-risk-level":"Critical"}}' +kubectl rollout restart deployment/ingress-nginx-controller -n ingress-nginx +kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx --timeout=60s + +echo "==> Step 5/6 : Add Helm repos" +helm repo add bitnami https://charts.bitnami.com/bitnami 2>/dev/null || true +helm repo add elastic https://helm.elastic.co 2>/dev/null || true +helm repo add graviteeio https://helm.gravitee.io 2>/dev/null || true helm repo update echo "==> Installing MongoDB" helm upgrade --install mongodb bitnami/mongodb \ --namespace "${NAMESPACE}" \ - --values "${SCRIPT_DIR}/values-mongodb.yaml" \ + --values "${SCRIPT_DIR}/mongo-values.yml" \ --wait --timeout 10m echo "==> Installing Elasticsearch" helm upgrade --install elasticsearch elastic/elasticsearch \ --namespace "${NAMESPACE}" \ - --values "${SCRIPT_DIR}/values-elasticsearch.yaml" \ + --values "${SCRIPT_DIR}/elastic-values.yml" \ --wait --timeout 10m -echo "==> Step 5/5 : Installing Gravitee APIM" +echo "==> Step 6/6 : Installing Gravitee APIM" helm upgrade --install graviteeio-apim graviteeio/apim \ --namespace "${NAMESPACE}" \ - --values "${SCRIPT_DIR}/values-apim.yaml" \ + --values "${SCRIPT_DIR}/apim-values.yml" \ --wait --timeout 15m echo "" @@ -76,15 +90,18 @@ echo "==> Deployment complete" echo "" kubectl get pods -n "${NAMESPACE}" echo "" -echo "Add to /etc/hosts (replace ):" -echo " gateway.gravitee.sttlab.pc console.gravitee.sttlab.pc portal.gravitee.sttlab.pc api.gravitee.sttlab.pc" +echo "Add to /etc/hosts (replace with your node IP):" +echo " console.gravitee.sttlab.pc portal.gravitee.sttlab.pc api.gravitee.sttlab.pc gateway.gravitee.sttlab.pc" echo "" -echo "URLs (HTTPS, self-signed CA - trust gravitee-ca-tls/ca.crt in your browser):" -echo " - Console : https://console.gravitee.sttlab.pc" -echo " - Portal : https://portal.gravitee.sttlab.pc" -echo " - API : https://api.gravitee.sttlab.pc/management" -echo " - Gateway : https://gateway.gravitee.sttlab.pc" -echo "" -echo "To export the CA cert for your trust store:" +echo "Import the CA into your browser:" echo " kubectl -n ${NAMESPACE} get secret gravitee-ca-tls -o jsonpath='{.data.ca\\.crt}' | base64 -d > gravitee-ca.crt" - +echo "" +echo "Admin credentials:" +echo " Username: admin" +echo " Password: $(kubectl -n ${NAMESPACE} get secret gravitee-admin -o jsonpath='{.data.admin-password-plain}' | base64 -d)" +echo "" +echo "URLs:" +echo " Console : https://console.gravitee.sttlab.pc" +echo " Portal : https://portal.gravitee.sttlab.pc" +echo " API : https://api.gravitee.sttlab.pc/management" +echo " Gateway : https://gateway.gravitee.sttlab.pc" diff --git a/secrets-create.sh b/secrets-create.sh index 2685912..a053a5a 100644 --- a/secrets-create.sh +++ b/secrets-create.sh @@ -50,8 +50,15 @@ kubectl -n "${NS}" create secret generic gravitee-jwt \ --from-literal=GRAVITEE_JWT_SECRET="${JWT_SECRET}" \ --dry-run=client -o yaml | kubectl apply -f - +echo "==> Creating CA trust secret for nginx ingress proxy-ssl-secret" +# Contains only ca.crt (no tls.crt/key) to avoid nginx presenting the CA as a client cert. +kubectl -n "${NS}" get secret gravitee-ca-tls -o jsonpath='{.data.ca\.crt}' | base64 -d | \ + kubectl -n "${NS}" create secret generic gravitee-ca-trust \ + --from-file=ca.crt=/dev/stdin \ + --dry-run=client -o yaml | kubectl apply -f - + echo "" echo "==> Done. Secrets created in namespace ${NS}:" -kubectl -n "${NS}" get secrets | grep -E 'mongodb-credentials|gravitee-mongodb-uri|gravitee-admin|gravitee-jwt' +kubectl -n "${NS}" get secrets | grep -E 'mongodb-credentials|gravitee-mongodb-uri|gravitee-admin|gravitee-jwt|gravitee-jks-password|gravitee-ca-trust' echo ""