first commit
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
# Gravitee APIM — k3s single-node deployment
|
||||
|
||||
Production-like deployment of Gravitee APIM OSS on k3s with end-to-end TLS, credentials injected from Kubernetes secrets, and an internal CA managed by cert-manager.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet
|
||||
└── nginx ingress (TLS termination + HTTPS re-encryption to backends)
|
||||
├── console.gravitee.sttlab.pc → graviteeio-apim-ui (nginx, port 8080)
|
||||
├── portal.gravitee.sttlab.pc → graviteeio-apim-portal (nginx, port 8080)
|
||||
├── api.gravitee.sttlab.pc → graviteeio-apim-api (Jetty/HTTPS, port 8083)
|
||||
└── gateway.gravitee.sttlab.pc → graviteeio-apim-gateway (Vert.x/HTTPS, port 8082)
|
||||
|
||||
Internal backends:
|
||||
graviteeio-apim-api ──TLS──► mongodb:27017
|
||||
graviteeio-apim-api ──TLS──► gravitee-es-master:9200
|
||||
graviteeio-apim-gateway ──TLS──► mongodb:27017
|
||||
graviteeio-apim-gateway ──TLS──► gravitee-es-master:9200
|
||||
```
|
||||
|
||||
**Stack:**
|
||||
- **Gravitee APIM 4.x** — Management API (Jetty), Gateway (Vert.x/Netty), Console UI and Developer Portal (Angular/nginx)
|
||||
- **Gravitee Kubernetes Operator (GKO)** — GitOps management of APIs via CRDs
|
||||
- **MongoDB 8.x** (Bitnami) — management and rate limiting persistence
|
||||
- **Elasticsearch 8.x** (Elastic) — analytics and reporting
|
||||
- **cert-manager** — internal self-signed PKI with automatic renewal
|
||||
- **nginx ingress** — external TLS termination + TLS proxy to backends
|
||||
|
||||
---
|
||||
|
||||
## PKI and certificates
|
||||
|
||||
### Trust chain
|
||||
|
||||
```
|
||||
gravitee-selfsigned-bootstrap (Issuer, self-signed)
|
||||
└── gravitee-ca (Certificate isCA:true) → secret: gravitee-ca-tls
|
||||
└── gravitee-ca-issuer (CA Issuer)
|
||||
├── console-tls (ingress console)
|
||||
├── portal-tls (ingress portal)
|
||||
├── api-tls (ingress api)
|
||||
├── gateway-tls (ingress gateway)
|
||||
├── mongodb-tls (MongoDB server)
|
||||
├── elasticsearch-tls (ES server + truststore.jks)
|
||||
├── api-internal-tls (Jetty API server + keystore.p12)
|
||||
└── gateway-internal-tls (Vert.x gateway server + keystore.p12)
|
||||
```
|
||||
|
||||
### cert-manager keystores
|
||||
|
||||
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.
|
||||
|
||||
### Notable SANs
|
||||
|
||||
- `api-internal-tls` includes `api.gravitee.sttlab.pc` — required for nginx → pod TLS verification
|
||||
- `gateway-internal-tls` includes `gateway.gravitee.sttlab.pc` — same reason
|
||||
- `elasticsearch-tls` includes `gravitee-es-master.gravitee-apim.svc.cluster.local`
|
||||
|
||||
### Browser trust
|
||||
|
||||
Export the CA for browser import:
|
||||
```bash
|
||||
kubectl get secret gravitee-ca-tls -n gravitee-apim \
|
||||
-o jsonpath='{.data.ca\.crt}' | base64 -d > gravitee-ca.crt
|
||||
```
|
||||
Import `gravitee-ca.crt` into the OS keychain or browser trust store.
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes secrets
|
||||
|
||||
All secrets are created by `secrets-create.sh` before the first deployment.
|
||||
|
||||
| Secret | Contents | Consumed by |
|
||||
|--------|----------|-------------|
|
||||
| `mongodb-credentials` | root/gravitee MongoDB passwords | Bitnami MongoDB chart |
|
||||
| `gravitee-mongodb-uri` | `GRAVITEE_MANAGEMENT_MONGODB_URI`, `GRAVITEE_RATELIMIT_MONGODB_URI` | api, gateway (Secret Manager) |
|
||||
| `gravitee-jwt` | `GRAVITEE_JWT_SECRET` | api (Secret Manager) |
|
||||
| `gravitee-admin` | `admin-password-plain`, `admin-password-bcrypt` | api (Secret Manager) |
|
||||
| `gravitee-es-master-credentials` | ES `username`, `password` | api, gateway (Secret Manager) |
|
||||
| `gravitee-encryption` | `api-properties-encryption-secret` | api, gateway (Secret Manager) |
|
||||
| `gravitee-jks-password` | keystore password | api, gateway (`JAVA_OPTS` env + Secret Manager for `ssl.keystore.password`) + cert-manager |
|
||||
| `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.
|
||||
|
||||
### Credential injection
|
||||
|
||||
Credentials are resolved at runtime by the **Gravitee Secret Manager** (Kubernetes provider, enabled via `secrets.kubernetes`). Helm values reference secrets using the `secret://kubernetes/<secret-name>:<key>` syntax, which is rendered verbatim into `gravitee.yml` and resolved by Gravitee on startup.
|
||||
|
||||
```yaml
|
||||
# In apim-values.yml — resolved by Gravitee Secret Manager at runtime
|
||||
jwtSecret: "secret://kubernetes/gravitee-jwt:GRAVITEE_JWT_SECRET"
|
||||
mongo:
|
||||
uri: "secret://kubernetes/gravitee-mongodb-uri:GRAVITEE_MANAGEMENT_MONGODB_URI"
|
||||
es:
|
||||
security:
|
||||
username: "secret://kubernetes/gravitee-es-master-credentials:username"
|
||||
password: "secret://kubernetes/gravitee-es-master-credentials:password"
|
||||
```
|
||||
|
||||
The only secret still injected as a Kubernetes env var is `gravitee-jks-password` (`JKS_PASSWORD`), because it is needed in `JAVA_OPTS` before the JVM starts — before the Gravitee Secret Manager is available.
|
||||
|
||||
---
|
||||
|
||||
## Internal TLS
|
||||
|
||||
### MongoDB
|
||||
|
||||
TLS enabled via `tls=true` in the connection URI. Server certificate validation is handled by the JVM truststore:
|
||||
```
|
||||
JAVA_OPTS: -Djavax.net.ssl.trustStoreType=PKCS12
|
||||
-Djavax.net.ssl.trustStore=/run/secrets/tls/truststore.p12
|
||||
-Djavax.net.ssl.trustStorePassword=${JKS_PASSWORD}
|
||||
```
|
||||
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`).
|
||||
|
||||
### Elasticsearch
|
||||
|
||||
HTTPS with basic auth. Server certificate signed by the Gravitee CA, validated by the same JVM truststore. The HTTPS endpoint and credentials are configured in `apim-values.yml` via `es.endpoints` / `es.security.*` and resolved at runtime by the Gravitee Secret Manager.
|
||||
|
||||
### nginx → backends (proxy TLS)
|
||||
|
||||
nginx verifies backend certificates:
|
||||
```yaml
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name api.gravitee.sttlab.pc;
|
||||
```
|
||||
|
||||
- `proxy-ssl-secret` provides the CA for chain verification
|
||||
- `proxy_ssl_name` (via snippet) tells nginx which hostname to check against the backend cert SANs
|
||||
- `proxy_ssl_server_name on` is intentionally **absent**: it modifies the TLS ClientHello in a way that triggers an `illegal parameter` alert in Jetty 12
|
||||
|
||||
Snippets require `annotations-risk-level: Critical` in the `ingress-nginx-controller` ConfigMap.
|
||||
|
||||
---
|
||||
|
||||
## UI ingress (SPA)
|
||||
|
||||
The console and portal are Angular SPAs. The Gravitee chart injects `rewrite-target: /$1` by default. Without a capture group in the path, all asset requests are rewritten to `/` (index.html served instead of CSS/JS).
|
||||
|
||||
Fix applied:
|
||||
```yaml
|
||||
path: /(.*)
|
||||
pathType: ImplementationSpecific
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- k3s with nginx ingress and cert-manager installed
|
||||
- Helm repos added: `bitnami`, `elastic`, `graviteeio`
|
||||
- `htpasswd` available (for admin BCrypt hash generation)
|
||||
|
||||
### First install
|
||||
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
Or step by step:
|
||||
|
||||
```bash
|
||||
# 1. Create credential secrets
|
||||
./secrets-create.sh
|
||||
|
||||
# 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. 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
|
||||
|
||||
# 6. Deploy Gravitee
|
||||
helm upgrade --install graviteeio-apim graviteeio/apim -n gravitee-apim -f apim-values.yml
|
||||
|
||||
# 7. Install GKO (after APIM is ready)
|
||||
# Create GKO credentials secret first (see GKO section below)
|
||||
helm upgrade --install gko graviteeio/gko \
|
||||
--namespace gravitee-apim \
|
||||
--set manager.httpClient.trustStore.path=/etc/ssl/certs/gravitee-ca.crt \
|
||||
--set-json 'manager.volumes=[{"name":"gravitee-ca","secret":{"secretName":"gravitee-ca-tls","items":[{"key":"ca.crt","path":"gravitee-ca.crt"}]}}]' \
|
||||
--set-json 'manager.volumeMounts=[{"name":"gravitee-ca","mountPath":"/etc/ssl/certs/gravitee-ca.crt","subPath":"gravitee-ca.crt","readOnly":true}]'
|
||||
|
||||
# 8. Apply GKO resources
|
||||
kubectl apply -f gko-management-context.yaml
|
||||
kubectl apply -f gko-api.yaml
|
||||
```
|
||||
|
||||
### Rebuild workloads only (secrets and certs preserved)
|
||||
|
||||
```bash
|
||||
helm uninstall graviteeio-apim mongodb elasticsearch -n gravitee-apim
|
||||
kubectl delete pvc -n gravitee-apim --all
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
```
|
||||
192.168.1.18 console.gravitee.sttlab.pc portal.gravitee.sttlab.pc api.gravitee.sttlab.pc gateway.gravitee.sttlab.pc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Access
|
||||
|
||||
| URL | Description |
|
||||
|-----|-------------|
|
||||
| https://console.gravitee.sttlab.pc | Management console |
|
||||
| https://portal.gravitee.sttlab.pc | Developer portal |
|
||||
| https://api.gravitee.sttlab.pc/management | Management API |
|
||||
| https://gateway.gravitee.sttlab.pc | Gateway (data plane) |
|
||||
|
||||
**Admin credentials:**
|
||||
```bash
|
||||
# Username: admin
|
||||
# Password (plaintext):
|
||||
kubectl get secret gravitee-admin -n gravitee-apim \
|
||||
-o jsonpath='{.data.admin-password-plain}' | base64 -d && echo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gravitee Kubernetes Operator (GKO)
|
||||
|
||||
GKO enables GitOps-driven API management: APIs, plans and subscriptions are declared as Kubernetes CRDs and automatically synced to the Management API.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
helm upgrade --install gko graviteeio/gko \
|
||||
--namespace gravitee-apim \
|
||||
--set manager.httpClient.trustStore.path=/etc/ssl/certs/gravitee-ca.crt \
|
||||
--set-json 'manager.volumes=[{"name":"gravitee-ca","secret":{"secretName":"gravitee-ca-tls","items":[{"key":"ca.crt","path":"gravitee-ca.crt"}]}}]' \
|
||||
--set-json 'manager.volumeMounts=[{"name":"gravitee-ca","mountPath":"/etc/ssl/certs/gravitee-ca.crt","subPath":"gravitee-ca.crt","readOnly":true}]'
|
||||
```
|
||||
|
||||
The Gravitee CA is mounted into the GKO pod so its HTTP client trusts the Management API's certificate (port 83, HTTPS). Without this, GKO would fail to connect when validating or reconciling CRDs.
|
||||
|
||||
### GKO secrets
|
||||
|
||||
| Secret | Contents | Purpose |
|
||||
|--------|----------|---------|
|
||||
| `gko-management-credentials` | `username`, `password` | GKO → Management API auth via `ManagementContext.spec.auth.secretRef` |
|
||||
|
||||
Create after deploying APIM:
|
||||
```bash
|
||||
ADMIN_PLAIN=$(kubectl get secret gravitee-admin -n gravitee-apim \
|
||||
-o jsonpath='{.data.admin-password-plain}' | base64 -d)
|
||||
kubectl create secret generic gko-management-credentials -n gravitee-apim \
|
||||
--from-literal=username=admin \
|
||||
--from-literal=password="${ADMIN_PLAIN}"
|
||||
```
|
||||
|
||||
### CRDs
|
||||
|
||||
| CRD | Description |
|
||||
|-----|-------------|
|
||||
| `ManagementContext` | Connection to a Gravitee Management API instance |
|
||||
| `ApiV4Definition` | Gravitee v4 API (proxy, message, native) |
|
||||
| `ApiDefinition` | Gravitee v2 API (legacy) |
|
||||
| `Application` | Consumer application |
|
||||
| `ApiResource` | Shared resource (cache, OAuth2, etc.) |
|
||||
| `SharedPolicyGroup` | Reusable policy group |
|
||||
| `Subscription` | API subscription |
|
||||
|
||||
### ManagementContext
|
||||
|
||||
`gko-management-context.yaml` — connects GKO to the local Management API:
|
||||
```yaml
|
||||
spec:
|
||||
baseUrl: https://graviteeio-apim-api.gravitee-apim.svc.cluster.local:83
|
||||
environmentId: DEFAULT
|
||||
organizationId: DEFAULT
|
||||
auth:
|
||||
secretRef:
|
||||
name: gko-management-credentials
|
||||
namespace: gravitee-apim
|
||||
```
|
||||
|
||||
> Port 83 is the Kubernetes service port (maps to container port 8083).
|
||||
|
||||
### Deploying an API
|
||||
|
||||
```bash
|
||||
kubectl apply -f gko-management-context.yaml
|
||||
kubectl apply -f gko-api.yaml
|
||||
kubectl get apiv4definition -n gravitee-apim
|
||||
```
|
||||
|
||||
The `processingStatus: Completed` and `state: STARTED` in the status confirm the API is live on the gateway.
|
||||
|
||||
---
|
||||
|
||||
## Security notes
|
||||
|
||||
- The Gravitee CA is self-signed. Replace with a Vault PKI Issuer in production (see comment in `certificates.yml`).
|
||||
- `proxy-ssl-verify: "on"` is enabled on all backend ingresses — nginx validates pod certificates.
|
||||
- Client mTLS is not enabled for MongoDB (complexity vs. benefit for intra-cluster traffic).
|
||||
- `annotations-risk-level: Critical` on the ingress controller is required for `proxy_ssl_name` snippets — document this in the ops runbook.
|
||||
@@ -0,0 +1,309 @@
|
||||
# Gravitee APIM OSS - prod-like single-node k3s deployment
|
||||
# - Domain: gravitee.sttlab.pc
|
||||
# - Ingress: nginx
|
||||
# - TLS everywhere (ingress + internal component HTTPS)
|
||||
# - Credentials sourced from pre-created secrets
|
||||
|
||||
adminAccountEnable: true
|
||||
adminPasswordBcrypt: "${GRAVITEE_ADMIN_PASSWORD_BCRYPT}"
|
||||
|
||||
# ============================================================
|
||||
# Kubernetes Secret Provider
|
||||
# ============================================================
|
||||
secrets:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
namespace: gravitee-apim
|
||||
timeoutMs: 3000
|
||||
|
||||
# ============================================================
|
||||
# API Gateway (data plane) - 2 replicas
|
||||
# ============================================================
|
||||
gateway:
|
||||
enabled: true
|
||||
replicaCount: 2
|
||||
|
||||
extraVolumes: |
|
||||
- name: gateway-internal-tls
|
||||
secret:
|
||||
secretName: gateway-internal-tls
|
||||
items:
|
||||
- key: keystore.p12
|
||||
path: keystore.p12
|
||||
- key: truststore.p12
|
||||
path: truststore.p12
|
||||
extraVolumeMounts: |
|
||||
- name: gateway-internal-tls
|
||||
mountPath: /run/secrets/tls
|
||||
readOnly: true
|
||||
|
||||
env:
|
||||
- name: GRAVITEE_MANAGEMENT_MONGODB_URI
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-mongodb-uri
|
||||
key: GRAVITEE_MANAGEMENT_MONGODB_URI
|
||||
- name: GRAVITEE_RATELIMIT_MONGODB_URI
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-mongodb-uri
|
||||
key: GRAVITEE_RATELIMIT_MONGODB_URI
|
||||
- name: GRAVITEE_REPORTERS_ELASTICSEARCH_ENDPOINTS_0
|
||||
value: "https://gravitee-es-master.gravitee-apim.svc.cluster.local:9200"
|
||||
- name: GRAVITEE_REPORTERS_ELASTICSEARCH_SECURITY_ENABLED
|
||||
value: "true"
|
||||
- name: GRAVITEE_REPORTERS_ELASTICSEARCH_SECURITY_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-es-master-credentials
|
||||
key: username
|
||||
- name: GRAVITEE_REPORTERS_ELASTICSEARCH_SECURITY_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-es-master-credentials
|
||||
key: password
|
||||
- name: JKS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
- name: GRAVITEE_API_PROPERTIES_ENCRYPTION_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-encryption
|
||||
key: api-properties-encryption-secret
|
||||
- name: JAVA_OPTS
|
||||
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:
|
||||
enabled: true
|
||||
keystore:
|
||||
type: pkcs12
|
||||
path: /run/secrets/tls/keystore.p12
|
||||
password: "${JKS_PASSWORD}"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
externalPort: 443
|
||||
internalPort: 8082
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name gateway.gravitee.sttlab.pc;
|
||||
hosts:
|
||||
- gateway.gravitee.sttlab.pc
|
||||
path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- gateway.gravitee.sttlab.pc
|
||||
secretName: gateway-tls
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
# ============================================================
|
||||
# Management API (control plane) - 1 replica
|
||||
# ============================================================
|
||||
api:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
extraVolumes: |
|
||||
- name: api-internal-tls
|
||||
secret:
|
||||
secretName: api-internal-tls
|
||||
items:
|
||||
- key: keystore.p12
|
||||
path: keystore.p12
|
||||
- key: truststore.p12
|
||||
path: truststore.p12
|
||||
extraVolumeMounts: |
|
||||
- name: api-internal-tls
|
||||
mountPath: /run/secrets/tls
|
||||
readOnly: true
|
||||
|
||||
env:
|
||||
- name: GRAVITEE_MANAGEMENT_MONGODB_URI
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-mongodb-uri
|
||||
key: GRAVITEE_MANAGEMENT_MONGODB_URI
|
||||
- name: GRAVITEE_RATELIMIT_MONGODB_URI
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-mongodb-uri
|
||||
key: GRAVITEE_RATELIMIT_MONGODB_URI
|
||||
- name: GRAVITEE_ANALYTICS_ELASTICSEARCH_ENDPOINTS_0
|
||||
value: "https://gravitee-es-master.gravitee-apim.svc.cluster.local:9200"
|
||||
- name: GRAVITEE_ANALYTICS_ELASTICSEARCH_SECURITY_ENABLED
|
||||
value: "true"
|
||||
- name: GRAVITEE_JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-jwt
|
||||
key: GRAVITEE_JWT_SECRET
|
||||
- name: GRAVITEE_ADMIN_PASSWORD_BCRYPT
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-admin
|
||||
key: admin-password-bcrypt
|
||||
- name: GRAVITEE_ANALYTICS_ELASTICSEARCH_SECURITY_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-es-master-credentials
|
||||
key: username
|
||||
- name: GRAVITEE_ANALYTICS_ELASTICSEARCH_SECURITY_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-es-master-credentials
|
||||
key: password
|
||||
- name: JKS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
- name: GRAVITEE_API_PROPERTIES_ENCRYPTION_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-encryption
|
||||
key: api-properties-encryption-secret
|
||||
- name: JAVA_OPTS
|
||||
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:
|
||||
services:
|
||||
core:
|
||||
http:
|
||||
enabled: true
|
||||
port: 18083
|
||||
host: 0.0.0.0
|
||||
|
||||
ssl:
|
||||
enabled: true
|
||||
keystore:
|
||||
type: pkcs12
|
||||
path: /run/secrets/tls/keystore.p12
|
||||
password: "${JKS_PASSWORD}"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 2Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
|
||||
ingress:
|
||||
management:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name api.gravitee.sttlab.pc;
|
||||
path: /management
|
||||
pathType: Prefix
|
||||
hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
tls:
|
||||
- hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
secretName: api-tls
|
||||
portal:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name api.gravitee.sttlab.pc;
|
||||
path: /portal
|
||||
pathType: Prefix
|
||||
hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
tls:
|
||||
- hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
secretName: api-tls
|
||||
|
||||
# ============================================================
|
||||
# Management UI (Console) - 1 replica
|
||||
# ============================================================
|
||||
ui:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
hosts:
|
||||
- console.gravitee.sttlab.pc
|
||||
path: /(.*)
|
||||
pathType: ImplementationSpecific
|
||||
tls:
|
||||
- hosts:
|
||||
- console.gravitee.sttlab.pc
|
||||
secretName: console-tls
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Developer Portal UI - 1 replica
|
||||
# ============================================================
|
||||
portal:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$1
|
||||
hosts:
|
||||
- portal.gravitee.sttlab.pc
|
||||
path: /(.*)
|
||||
pathType: ImplementationSpecific
|
||||
tls:
|
||||
- hosts:
|
||||
- portal.gravitee.sttlab.pc
|
||||
secretName: portal-tls
|
||||
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
# Gravitee APIM OSS - prod-like single-node k3s deployment
|
||||
# - Domain: gravitee.sttlab.pc
|
||||
# - Ingress: nginx
|
||||
# - TLS everywhere (ingress + internal component HTTPS)
|
||||
# - Credentials resolved at runtime via Gravitee Secret Manager (kubernetes provider)
|
||||
|
||||
adminAccountEnable: true
|
||||
adminPasswordBcrypt: "secret://kubernetes/gravitee-admin:admin-password-bcrypt"
|
||||
|
||||
jwtSecret: "secret://kubernetes/gravitee-jwt:GRAVITEE_JWT_SECRET"
|
||||
|
||||
# ============================================================
|
||||
# MongoDB (management + ratelimit, same URI)
|
||||
# ============================================================
|
||||
mongo:
|
||||
uri: "secret://kubernetes/gravitee-mongodb-uri:GRAVITEE_MANAGEMENT_MONGODB_URI"
|
||||
|
||||
# ============================================================
|
||||
# Elasticsearch
|
||||
# ============================================================
|
||||
es:
|
||||
endpoints:
|
||||
- "https://gravitee-es-master.gravitee-apim.svc.cluster.local:9200"
|
||||
security:
|
||||
enabled: true
|
||||
username: "secret://kubernetes/gravitee-es-master-credentials:username"
|
||||
password: "secret://kubernetes/gravitee-es-master-credentials:password"
|
||||
|
||||
# ============================================================
|
||||
# Kubernetes Secret Provider
|
||||
# ============================================================
|
||||
secrets:
|
||||
kubernetes:
|
||||
enabled: true
|
||||
namespace: gravitee-apim
|
||||
timeoutMs: 3000
|
||||
|
||||
# ============================================================
|
||||
# API Gateway (data plane) - 2 replicas
|
||||
# ============================================================
|
||||
gateway:
|
||||
enabled: true
|
||||
replicaCount: 2
|
||||
|
||||
api:
|
||||
properties:
|
||||
encryption:
|
||||
secret: "secret://kubernetes/gravitee-encryption:api-properties-encryption-secret"
|
||||
|
||||
extraVolumes: |
|
||||
- name: gateway-internal-tls
|
||||
secret:
|
||||
secretName: gateway-internal-tls
|
||||
items:
|
||||
- key: keystore.p12
|
||||
path: keystore.p12
|
||||
- key: truststore.p12
|
||||
path: truststore.p12
|
||||
extraVolumeMounts: |
|
||||
- name: gateway-internal-tls
|
||||
mountPath: /run/secrets/tls
|
||||
readOnly: true
|
||||
|
||||
env:
|
||||
- name: JKS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
- name: JAVA_OPTS
|
||||
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:
|
||||
enabled: true
|
||||
keystore:
|
||||
type: pkcs12
|
||||
path: /run/secrets/tls/keystore.p12
|
||||
password: "secret://kubernetes/gravitee-jks-password:password"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
externalPort: 443
|
||||
internalPort: 8082
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name gateway.gravitee.sttlab.pc;
|
||||
hosts:
|
||||
- gateway.gravitee.sttlab.pc
|
||||
path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- gateway.gravitee.sttlab.pc
|
||||
secretName: gateway-tls
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
# ============================================================
|
||||
# Management API (control plane) - 1 replica
|
||||
# ============================================================
|
||||
api:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
api:
|
||||
properties:
|
||||
encryption:
|
||||
secret: "secret://kubernetes/gravitee-encryption:api-properties-encryption-secret"
|
||||
|
||||
extraVolumes: |
|
||||
- name: api-internal-tls
|
||||
secret:
|
||||
secretName: api-internal-tls
|
||||
items:
|
||||
- key: keystore.p12
|
||||
path: keystore.p12
|
||||
- key: truststore.p12
|
||||
path: truststore.p12
|
||||
extraVolumeMounts: |
|
||||
- name: api-internal-tls
|
||||
mountPath: /run/secrets/tls
|
||||
readOnly: true
|
||||
|
||||
env:
|
||||
- name: JKS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
- name: JAVA_OPTS
|
||||
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:
|
||||
services:
|
||||
core:
|
||||
http:
|
||||
enabled: true
|
||||
port: 18083
|
||||
host: 0.0.0.0
|
||||
|
||||
ssl:
|
||||
enabled: true
|
||||
keystore:
|
||||
type: pkcs12
|
||||
path: /run/secrets/tls/keystore.p12
|
||||
password: "secret://kubernetes/gravitee-jks-password:password"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 2Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
|
||||
ingress:
|
||||
management:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name api.gravitee.sttlab.pc;
|
||||
path: /management
|
||||
pathType: Prefix
|
||||
hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
tls:
|
||||
- hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
secretName: api-tls
|
||||
portal:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-secret: "gravitee-apim/gravitee-ca-trust"
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
proxy_ssl_name api.gravitee.sttlab.pc;
|
||||
path: /portal
|
||||
pathType: Prefix
|
||||
hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
tls:
|
||||
- hosts:
|
||||
- api.gravitee.sttlab.pc
|
||||
secretName: api-tls
|
||||
|
||||
# ============================================================
|
||||
# Management UI (Console) - 1 replica
|
||||
# ============================================================
|
||||
ui:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
hosts:
|
||||
- console.gravitee.sttlab.pc
|
||||
path: /(.*)
|
||||
pathType: ImplementationSpecific
|
||||
tls:
|
||||
- hosts:
|
||||
- console.gravitee.sttlab.pc
|
||||
secretName: console-tls
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Developer Portal UI - 1 replica
|
||||
# ============================================================
|
||||
portal:
|
||||
enabled: true
|
||||
replicaCount: 1
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$1
|
||||
hosts:
|
||||
- portal.gravitee.sttlab.pc
|
||||
path: /(.*)
|
||||
pathType: ImplementationSpecific
|
||||
tls:
|
||||
- hosts:
|
||||
- portal.gravitee.sttlab.pc
|
||||
secretName: portal-tls
|
||||
@@ -0,0 +1,202 @@
|
||||
# cert-manager configuration for Gravitee APIM
|
||||
# Self-signed CA + namespace-scoped Issuer
|
||||
#
|
||||
# To switch to HashiCorp Vault later:
|
||||
# 1. Replace `gravitee-ca-issuer` (Issuer kind: CA) with a Vault Issuer:
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Issuer
|
||||
# metadata: {name: gravitee-ca-issuer, namespace: gravitee-apim}
|
||||
# spec:
|
||||
# vault:
|
||||
# server: https://vault.sttlab.pc:8200
|
||||
# path: pki/sign/gravitee
|
||||
# auth: { kubernetes: { ... } }
|
||||
# 2. Keep the Certificate resources below unchanged - they reference
|
||||
# `gravitee-ca-issuer` by name, so the swap is transparent.
|
||||
---
|
||||
# Step 1: bootstrap a self-signed Issuer (only used to sign the CA cert)
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: gravitee-selfsigned-bootstrap
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
selfSigned: {}
|
||||
---
|
||||
# Step 2: create a CA certificate signed by the bootstrap issuer
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: gravitee-ca
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
isCA: true
|
||||
commonName: gravitee-ca.sttlab.pc
|
||||
secretName: gravitee-ca-tls
|
||||
duration: 87600h # 10 years
|
||||
privateKey:
|
||||
algorithm: RSA
|
||||
size: 4096
|
||||
issuerRef:
|
||||
name: gravitee-selfsigned-bootstrap
|
||||
kind: Issuer
|
||||
group: cert-manager.io
|
||||
---
|
||||
# Step 3: the actual CA Issuer used by all Gravitee certs
|
||||
# This is the resource to replace when integrating Vault PKI later
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: gravitee-ca-issuer
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
ca:
|
||||
secretName: gravitee-ca-tls
|
||||
---
|
||||
# ----------------------------
|
||||
# Ingress certificates (one per public host)
|
||||
# ----------------------------
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: console-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: console-tls
|
||||
dnsNames:
|
||||
- console.gravitee.sttlab.pc
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: portal-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: portal-tls
|
||||
dnsNames:
|
||||
- portal.gravitee.sttlab.pc
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: api-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: api-tls
|
||||
dnsNames:
|
||||
- api.gravitee.sttlab.pc
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: gateway-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: gateway-tls
|
||||
dnsNames:
|
||||
- gateway.gravitee.sttlab.pc
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
---
|
||||
# ----------------------------
|
||||
# Internal TLS server certs (cluster.local hostnames)
|
||||
# ----------------------------
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: mongodb-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: mongodb-tls
|
||||
commonName: mongodb.gravitee-apim.svc.cluster.local
|
||||
dnsNames:
|
||||
- mongodb
|
||||
- mongodb.gravitee-apim
|
||||
- mongodb.gravitee-apim.svc
|
||||
- mongodb.gravitee-apim.svc.cluster.local
|
||||
- mongodb-0.mongodb-headless.gravitee-apim.svc.cluster.local
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: elasticsearch-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: elasticsearch-tls
|
||||
commonName: gravitee-es-master.gravitee-apim.svc.cluster.local
|
||||
dnsNames:
|
||||
- gravitee-es-master
|
||||
- gravitee-es-master.gravitee-apim
|
||||
- gravitee-es-master.gravitee-apim.svc
|
||||
- gravitee-es-master.gravitee-apim.svc.cluster.local
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
keystores:
|
||||
jks:
|
||||
create: true
|
||||
passwordSecretRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: gateway-internal-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: gateway-internal-tls
|
||||
commonName: graviteeio-apim-gateway.gravitee-apim.svc.cluster.local
|
||||
dnsNames:
|
||||
- graviteeio-apim-gateway
|
||||
- graviteeio-apim-gateway.gravitee-apim
|
||||
- graviteeio-apim-gateway.gravitee-apim.svc
|
||||
- graviteeio-apim-gateway.gravitee-apim.svc.cluster.local
|
||||
- gateway.gravitee.sttlab.pc
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
keystores:
|
||||
pkcs12:
|
||||
create: true
|
||||
passwordSecretRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: api-internal-tls
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
secretName: api-internal-tls
|
||||
commonName: graviteeio-apim-api.gravitee-apim.svc.cluster.local
|
||||
dnsNames:
|
||||
- graviteeio-apim-api
|
||||
- graviteeio-apim-api.gravitee-apim
|
||||
- graviteeio-apim-api.gravitee-apim.svc
|
||||
- graviteeio-apim-api.gravitee-apim.svc.cluster.local
|
||||
- api.gravitee.sttlab.pc
|
||||
issuerRef:
|
||||
name: gravitee-ca-issuer
|
||||
kind: Issuer
|
||||
keystores:
|
||||
pkcs12:
|
||||
create: true
|
||||
passwordSecretRef:
|
||||
name: gravitee-jks-password
|
||||
key: password
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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 by secrets-create.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAMESPACE="gravitee-apim"
|
||||
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 htpasswd >/dev/null || { echo "htpasswd not found (required for admin BCrypt hash)"; exit 1; }
|
||||
|
||||
echo "==> Verifying cluster reachable"
|
||||
kubectl cluster-info
|
||||
|
||||
echo "==> Verifying cert-manager is installed"
|
||||
kubectl get crd certificates.cert-manager.io >/dev/null 2>&1 || {
|
||||
echo "ERROR: cert-manager CRDs not found. Install cert-manager first."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "==> Verifying nginx ingress controller is installed"
|
||||
kubectl get ingressclass nginx >/dev/null 2>&1 || {
|
||||
echo "WARNING: 'nginx' IngressClass not found. Ensure nginx-ingress is installed."
|
||||
}
|
||||
|
||||
echo "==> Step 1/6 : Create namespace"
|
||||
kubectl create namespace "${NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
echo "==> Step 2/6 : Create credential secrets (first install only)"
|
||||
if kubectl -n "${NAMESPACE}" get secret gravitee-admin >/dev/null 2>&1; then
|
||||
echo " Secrets already exist — skipping. Delete them manually to recreate."
|
||||
else
|
||||
"${SCRIPT_DIR}/secrets-create.sh"
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
echo "==> Waiting for all Certificates to be Ready"
|
||||
for cert in gravitee-ca console-tls portal-tls api-tls gateway-tls \
|
||||
mongodb-tls elasticsearch-tls gateway-internal-tls api-internal-tls; do
|
||||
echo " - waiting for ${cert}"
|
||||
kubectl -n "${NAMESPACE}" wait --for=condition=Ready "certificate/${cert}" --timeout=180s
|
||||
done
|
||||
|
||||
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}/mongo-values.yml" \
|
||||
--wait --timeout 10m
|
||||
|
||||
echo "==> Installing Elasticsearch"
|
||||
helm upgrade --install elasticsearch elastic/elasticsearch \
|
||||
--namespace "${NAMESPACE}" \
|
||||
--values "${SCRIPT_DIR}/elastic-values.yml" \
|
||||
--wait --timeout 10m
|
||||
|
||||
echo "==> Step 6/6 : Installing Gravitee APIM"
|
||||
helm upgrade --install graviteeio-apim graviteeio/apim \
|
||||
--namespace "${NAMESPACE}" \
|
||||
--values "${SCRIPT_DIR}/apim-values.yml" \
|
||||
--wait --timeout 15m
|
||||
|
||||
echo ""
|
||||
echo "==> Deployment complete"
|
||||
echo ""
|
||||
kubectl get pods -n "${NAMESPACE}"
|
||||
echo ""
|
||||
echo "Add to /etc/hosts (replace <NODE_IP> with your node IP):"
|
||||
echo " <NODE_IP> console.gravitee.sttlab.pc portal.gravitee.sttlab.pc api.gravitee.sttlab.pc gateway.gravitee.sttlab.pc"
|
||||
echo ""
|
||||
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"
|
||||
@@ -0,0 +1,65 @@
|
||||
# Elasticsearch - single node with TLS on HTTP
|
||||
clusterName: "gravitee-es"
|
||||
nodeGroup: "master"
|
||||
|
||||
replicas: 1
|
||||
minimumMasterNodes: 1
|
||||
|
||||
# Mount the cert-manager-issued cert
|
||||
secretMounts:
|
||||
- name: elasticsearch-tls
|
||||
secretName: elasticsearch-tls
|
||||
path: /usr/share/elasticsearch/config/tls
|
||||
defaultMode: "0755"
|
||||
|
||||
esConfig:
|
||||
elasticsearch.yml: |
|
||||
cluster.name: gravitee-es
|
||||
network.host: 0.0.0.0
|
||||
xpack.security.enabled: true
|
||||
xpack.security.http.ssl.enabled: true
|
||||
xpack.security.http.ssl.certificate: /usr/share/elasticsearch/config/tls/tls.crt
|
||||
xpack.security.http.ssl.key: /usr/share/elasticsearch/config/tls/tls.key
|
||||
xpack.security.http.ssl.certificate_authorities: /usr/share/elasticsearch/config/tls/ca.crt
|
||||
xpack.security.transport.ssl.enabled: true
|
||||
xpack.security.transport.ssl.verification_mode: certificate
|
||||
xpack.security.transport.ssl.certificate: /usr/share/elasticsearch/config/tls/tls.crt
|
||||
xpack.security.transport.ssl.key: /usr/share/elasticsearch/config/tls/tls.key
|
||||
xpack.security.transport.ssl.certificate_authorities: /usr/share/elasticsearch/config/tls/ca.crt
|
||||
|
||||
# Tell the chart that HTTPS is in use for readiness probes
|
||||
protocol: https
|
||||
# Disable cert verification on probes (self-signed)
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
curl -k -s --fail -u "elastic:${ELASTIC_PASSWORD}" https://127.0.0.1:9200/_cluster/health?local=true
|
||||
|
||||
esJavaOpts: "-Xms512m -Xmx512m"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
|
||||
volumeClaimTemplate:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
|
||||
createCert: false
|
||||
|
||||
sysctlInitContainer:
|
||||
enabled: false
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: gravitee-init-settings
|
||||
namespace: gravitee-apim
|
||||
annotations:
|
||||
helm.sh/hook: post-install,post-upgrade
|
||||
helm.sh/hook-weight: "10"
|
||||
helm.sh/hook-delete-policy: before-hook-creation
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: init-settings
|
||||
image: curlimages/curl:latest
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
MGMT_URL="https://graviteeio-apim-api.gravitee-apim.svc.cluster.local:83"
|
||||
|
||||
echo "Waiting for Management API..."
|
||||
until curl -sk -u "${ADMIN_USER}:${ADMIN_PASSWORD}" \
|
||||
"${MGMT_URL}/management/v2/ui/bootstrap" | grep -q "baseURL"; do
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "Fetching current settings..."
|
||||
SETTINGS=$(curl -sk -u "${ADMIN_USER}:${ADMIN_PASSWORD}" \
|
||||
"${MGMT_URL}/management/organizations/DEFAULT/environments/DEFAULT/settings")
|
||||
|
||||
echo "Updating portal entrypoint..."
|
||||
UPDATED=$(echo "$SETTINGS" | sed 's|"entrypoint":"[^"]*"|"entrypoint":"https://gateway.gravitee.sttlab.pc"|')
|
||||
|
||||
curl -sk -u "${ADMIN_USER}:${ADMIN_PASSWORD}" \
|
||||
-X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$UPDATED" \
|
||||
"${MGMT_URL}/management/organizations/DEFAULT/environments/DEFAULT/settings"
|
||||
|
||||
echo "Done."
|
||||
env:
|
||||
- name: ADMIN_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-admin
|
||||
key: admin-username
|
||||
- name: ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gravitee-admin
|
||||
key: admin-password-plain
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gravitee.io/v1alpha1
|
||||
kind: ManagementContext
|
||||
metadata:
|
||||
name: gravitee-management-context
|
||||
namespace: gravitee-apim
|
||||
spec:
|
||||
baseUrl: https://graviteeio-apim-api.gravitee-apim.svc.cluster.local:83
|
||||
environmentId: DEFAULT
|
||||
organizationId: DEFAULT
|
||||
auth:
|
||||
secretRef:
|
||||
name: gko-management-credentials
|
||||
namespace: gravitee-apim
|
||||
@@ -0,0 +1,44 @@
|
||||
# MongoDB - standalone with TLS, credentials sourced from existing secret
|
||||
architecture: standalone
|
||||
|
||||
auth:
|
||||
enabled: true
|
||||
rootUser: root
|
||||
usernames:
|
||||
- gravitee
|
||||
databases:
|
||||
- gravitee
|
||||
# Reference pre-created secret instead of inline passwords
|
||||
existingSecret: mongodb-credentials
|
||||
|
||||
# Enable TLS on MongoDB server
|
||||
tls:
|
||||
enabled: true
|
||||
autoGenerated: false
|
||||
mTLS:
|
||||
enabled: true
|
||||
standalone:
|
||||
existingSecret: mongodb-tls
|
||||
|
||||
extraFlags:
|
||||
- "--tlsAllowConnectionsWithoutCertificates"
|
||||
|
||||
updateStrategy:
|
||||
type: Recreate
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 8Gi
|
||||
storageClass: local-path
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create all credential secrets before the first helm install.
|
||||
# Skips any secret that already exists — delete it first to regenerate.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NS="gravitee-apim"
|
||||
|
||||
# Ensure namespace exists
|
||||
kubectl create namespace "${NS}" --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
secret_exists() {
|
||||
kubectl -n "${NS}" get secret "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
echo "==> Creating MongoDB credentials"
|
||||
if secret_exists mongodb-credentials; then
|
||||
echo " mongodb-credentials already exists, skipping"
|
||||
else
|
||||
MONGO_ROOT_PASSWORD=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 16)
|
||||
MONGO_GRAVITEE_PASSWORD=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 16)
|
||||
kubectl -n "${NS}" create secret generic mongodb-credentials \
|
||||
--from-literal=mongodb-root-password="${MONGO_ROOT_PASSWORD}" \
|
||||
--from-literal=mongodb-passwords="${MONGO_GRAVITEE_PASSWORD}" \
|
||||
--from-literal=mongodb-replica-set-key=''
|
||||
fi
|
||||
|
||||
echo "==> Creating MongoDB URI secret"
|
||||
if secret_exists gravitee-mongodb-uri; then
|
||||
echo " gravitee-mongodb-uri already exists, skipping"
|
||||
else
|
||||
MONGO_GRAVITEE_PASSWORD=$(kubectl -n "${NS}" get secret mongodb-credentials \
|
||||
-o jsonpath='{.data.mongodb-passwords}' | base64 -d)
|
||||
MONGO_URI="mongodb://gravitee:${MONGO_GRAVITEE_PASSWORD}@mongodb.gravitee-apim.svc.cluster.local:27017/gravitee?tls=true&authSource=gravitee"
|
||||
kubectl -n "${NS}" create secret generic gravitee-mongodb-uri \
|
||||
--from-literal=GRAVITEE_MANAGEMENT_MONGODB_URI="${MONGO_URI}" \
|
||||
--from-literal=GRAVITEE_RATELIMIT_MONGODB_URI="${MONGO_URI}"
|
||||
fi
|
||||
|
||||
echo "==> Creating Gravitee admin credentials"
|
||||
if secret_exists gravitee-admin; then
|
||||
echo " gravitee-admin already exists, skipping"
|
||||
else
|
||||
GRAVITEE_ADMIN_PASSWORD=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 16)
|
||||
ADMIN_BCRYPT=$(htpasswd -bnBC 10 "" "${GRAVITEE_ADMIN_PASSWORD}" | tr -d ':\n')
|
||||
kubectl -n "${NS}" create secret generic gravitee-admin \
|
||||
--from-literal=admin-username='admin' \
|
||||
--from-literal=admin-password-plain="${GRAVITEE_ADMIN_PASSWORD}" \
|
||||
--from-literal=admin-password-bcrypt="${ADMIN_BCRYPT}"
|
||||
fi
|
||||
|
||||
echo "==> Creating JKS keystore password"
|
||||
if secret_exists gravitee-jks-password; then
|
||||
echo " gravitee-jks-password already exists, skipping"
|
||||
else
|
||||
JKS_PASSWORD=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 20)
|
||||
kubectl -n "${NS}" create secret generic gravitee-jks-password \
|
||||
--from-literal=password="${JKS_PASSWORD}"
|
||||
fi
|
||||
|
||||
echo "==> Creating JWT signing secret"
|
||||
if secret_exists gravitee-jwt; then
|
||||
echo " gravitee-jwt already exists, skipping"
|
||||
else
|
||||
JWT_SECRET=$(openssl rand -base64 48 | tr -d '\n')
|
||||
kubectl -n "${NS}" create secret generic gravitee-jwt \
|
||||
--from-literal=GRAVITEE_JWT_SECRET="${JWT_SECRET}"
|
||||
fi
|
||||
|
||||
echo "==> Creating API properties encryption key"
|
||||
if secret_exists gravitee-encryption; then
|
||||
echo " gravitee-encryption already exists, skipping"
|
||||
else
|
||||
ENCRYPTION_KEY=$(openssl rand -hex 16)
|
||||
kubectl -n "${NS}" create secret generic gravitee-encryption \
|
||||
--from-literal=api-properties-encryption-secret="${ENCRYPTION_KEY}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "==> Done. Secrets in namespace ${NS}:"
|
||||
kubectl -n "${NS}" get secrets | grep -E 'mongodb-credentials|gravitee-mongodb-uri|gravitee-admin|gravitee-jwt|gravitee-jks-password|gravitee-ca-trust'
|
||||
echo ""
|
||||
Reference in New Issue
Block a user