Grizzly with TLS deprecated
This guide explains how to enable HTTPS in addition to HTTP for Grizzly. The guide follows the assumption that your deployment has been set up with the OX App Suite Stack Chart, and you are running Istio.
Istio
Any given request to an Istio gateway will have two connections.
- The inbound request, initiated by some client such as OX App Suite UI. This is often called the
downstream
connection. - The outbound request, initiated by the gateway to some backend. This is often called the
upstream
connection.
Both of these connections have independent TLS configurations.¹ Usually, securing the inbound traffic is sufficient. TLS will be terminated at the Gateway, and the outbound traffic will be sent upstream unencrypted. For enhanced security, it is possible to encrypt the upstream traffic too.
This guide will describe how to secure the upstream traffic to Grizzly.
References:
¹ Istio Documentation - Gateway TLS Configuration
Outbound Encryption
Outbound encryption is configured by the TLS settings of DestinationRules
. The OX App Suite Stack Chart configures DestinationRules
for all core-mw
services that are accessible by end-users. Those services are:
<RELEASE>-core-mw-http-api
<RELEASE>-core-mw-sync
<RELEASE>-core-mw-businessmobility
In order to enable TLS for outbound connections, set the TLS mode
to SIMPLE
. Moreover, changing the destination port of the VirtualServices
to 443
is helpful to emphasize that TLS is being used:
appsuite:
core-mw:
istio:
virtualServices:
destinationPort: 443
istio:
destinationRules:
appsuite:
tls:
mode: SIMPLE
dav:
tls:
mode: SIMPLE
businessmobility:
tls:
mode: SIMPLE
Certificates
After enabling outbound encryption, we need to create certificates for all services. This section will guide you through the process.
Prerequisites
Create Certificates
The following script generates RSA 4096-bit key pairs with self-signed certificates for each service. The key pairs and certificates are stored within a password-protected PKCS#12 keystore, which will be used by the open-xchange
daemon. Ensure that the release name, namespace, and services match your deployment. Additionally, you can adjust parameters such as algorithm, key strength, validity, and keystore password as desired.
#!/bin/bash -xe
release=example-release
namespace=example-namespace
services=(
${release}-core-mw-http-api
${release}-core-mw-sync
${release}-core-mw-admin
${release}-core-mw-businessmobility
${release}-core-mw-request-analyzer
)
# Create a certificate authority
cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
"CN": "Open-Xchange Certificate Authority",
"key": {
"algo": "rsa",
"size": 4096
}
}
EOF
# Create signing configuration
cat > signing-config.json<< EOF
{
"signing": {
"default": {
"usages": [
"digital signature",
"key encipherment",
"server auth"
],
"expiry": "876000h",
"ca_constraint": {
"is_ca": false
}
}
}
}
EOF
for service in ${services[@]}; do
# Generate a private key and certificate signing request
cat <<EOF | cfssl genkey - | cfssljson -bare ${service}
{
"hosts": [
"${service}.${namespace}.svc.cluster.local"
],
"CN": "${service}.${namespace}.svc.cluster.local",
"key": {
"algo": "ecdsa",
"size": 256
}
}
EOF
# Generate a CSR manifest and send it to the API server
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: ${service}
spec:
request: $(cat ${service}.csr | base64 | tr -d '\n')
signerName: open-xchange.com/signer
usages:
- digital signature
- key encipherment
- server auth
EOF
# Approve the certificate signing request
kubectl certificate approve ${service}
# Sign the certificate request
kubectl get csr ${service} -o jsonpath='{.spec.request}' | \
base64 --decode | \
cfssl sign -ca ca.pem -ca-key ca-key.pem -config signing-config.json - | \
cfssljson -bare ca-signed-${service}
# Upload the signed certificate
kubectl get csr ${service} -o json | \
jq '.status.certificate = "'$(base64 ca-signed-${service}.pem | tr -d '\n')'"' | \
kubectl replace --raw "/apis/certificates.k8s.io/v1/certificatesigningrequests/${service}/status" -f -
# Download the issued certificate and save it to a ${service}.crt file
kubectl get csr ${service} -o jsonpath='{.status.certificate}' \
| base64 --decode > ${service}.crt
# Import certificate to Java keystore
keytool -importcert -keystore grizzly.jks -trustcacerts -alias ${service}.${namespace}.svc.cluster.local -keypass secret -storepass secret -file ${service}.crt -storetype PKCS12 -noprompt
done
For more information about managing TLS in a cluster, please refer to the Kubernetes documentation
Grizzly Configuration
Grizzly can be configured via the core-mw
Helm chart, which is part of the OX App Suite Stack Chart.
appsuite:
core-mw:
properties:
com.openexchange.http.grizzly.hasSSLEnabled: "true"
com.openexchange.http.grizzly.keystorePath: "/opt/open-xchange/etc/grizzly.jks"
com.openexchange.http.grizzly.keystorePassword: "secret"
etcBinaries:
- name: grizzly
filename: grizzly.jks
b64Content: <YOUR_BASE64_ENCODED_KEYSTORE>
This configuration makes Grizzly listen on port 8010
in addition to 8009
, with 8010
accepting HTTPS connections only.
To expose port 8010
for services, we need to update the container ports:
appsuite:
core-mw:
containerPorts:
- containerPort: 8009
name: http
- containerPort: 8010
name: https
After that, we can modify the services created by the core-mw
chart. The services must accept the traffic on port 443
to match the destination port of the VirtualServices
. The target port needs to be set to 8010
to match the exposed container port.
appsuite:
core-mw:
roles:
http-api:
services:
- type: ClusterIP
ports:
- port: 443
targetPort: 8010
protocol: TCP
name: https
sync:
services:
- type: ClusterIP
ports:
- port: 443
targetPort: 8010
protocol: TCP
name: https
admin:
services:
- type: ClusterIP
ports:
- port: 443
targetPort: 8010
protocol: TCP
name: https
businessmobility:
services:
- type: ClusterIP
ports:
- port: 443
targetPort: 8010
protocol: TCP
name: https
values:
features:
status:
usm-eas: enabled
properties:
com.openexchange.usm.ox.url: http://localhost:8009/appsuite/api/
request-analyzer:
services:
- type: ClusterIP
ports:
- port: 443
targetPort: 8010
protocol: TCP
name: https
hazelcast-data-holding:
controller: "StatefulSet"
statefulSetServiceName: "hazelcast-headless"
services:
- name: hazelcast-headless
headless: true
ports:
- name: tcp-hazelcast
port: 5701
hazelcast-lite-member: {}
Optionally, you can adjust the readiness and startup probes to the secure port:
appsuite:
core-mw:
probe:
readiness:
httpGet:
scheme: HTTPS
port: 8010
startup:
httpGet:
scheme: HTTPS
port: 8010