seamlessly encrypt traffic from any apps in your mesh to memorystore (redis)
Update on Sep 8th, 2022: this blog article is now on Medium.
Note: this blog article is an end-to-end recipe implementing the Istio TLS origination to secure Memorystore (redis) access blog article I wrote a few months ago.
Anthos Service Mesh (ASM), a managed Istio implementation, can improve your security posture for your Kubernetes clusters and your apps. Istio aims to bring as much value to users out of the box without any configuration at all. ASM, on top of that, simplifies all the management of both the control plane and the data plane and the integration with monitoring and logging. Without any compromise, your apps in your Mesh will benefit from advanced features like traffic encryption, logging, tracing, etc. without updating the code of your apps.
In this article, we will show how easy and seamless it is to encrypt traffic from the Online Boutique sample apps to Memorystore (redis) by leveraging Anthos Service Mesh (ASM).
Objectives
- Create a Google Kubernetes Engine (GKE) cluster
- Deploy the Online Boutique sample apps with an in-cluster redis database
- Provision a Memorystore (redis) instance allowing only in-transit encryption
- Connect the Online Boutique sample apps to the Memorystore (redis) instance
- Install the managed Anthos Service Mesh (ASM) on this cluster
- Configure the TLS origination from the Mesh to the Memorystore (redis) instance
- Verify that the Online Boutique sample apps is successfully communicating over TLS with the Memorystore (redis) instance
Costs
This tutorial uses billable components of Google Cloud, including the following:
Use the pricing calculator to generate a cost estimate based on your projected usage.
Before you begin
This guide assumes that you have owner IAM permissions for your Google Cloud project. In production, you do not require owner permission.
Verify that billing is enabled for your project.
Set up your environment
Initialize the common variables used throughout this tutorial:
PROJECT_ID=FIXME-WITH-YOUR-PROJECT-ID
REGION=us-east4
ZONE=us-east4-a
PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)')
To avoid repeating the --project
in the commands throughout this tutorial, let’s set the current project:
gcloud config set project ${PROJECT_ID}
Enable the required APIs in your project
Enable the required APIs in your project:
gcloud services enable \
redis.googleapis.com \
mesh.googleapis.com
Create a GKE cluster
Create a GKE cluster:
CLUSTER=redis-tls-tutorial
gcloud container clusters create ${CLUSTER} \
--zone ${ZONE} \
--machine-type=e2-standard-4 \
--num-nodes 4 \
--workload-pool ${PROJECT_ID}.svc.id.goog \
--labels mesh_id=proj-${PROJECT_NUMBER} \
--network default
Deploy the Online Boutique sample apps
Create a dedicated Namespace
:
NAMESPACE=onlineboutique
kubectl create ns ${NAMESPACE}
Deploy the Online Boutique sample apps:
kubectl apply \
-f https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/main/release/kubernetes-manifests.yaml \
-n ${NAMESPACE}
After all the apps are successfully deployed, you could navigate to the Online Boutique website by clicking on the link below:
echo -n "http://" && kubectl get svc frontend-external -n ${NAMESPACE} -o json | jq -r '.status.loadBalancer.ingress[0].ip'
Provision a Memorystore (redis) instance allowing only in-transit encryption
Provision the Memorystore (redis) instance allowing only in-transit encryption:
REDIS_NAME=redis-tls-tutorial
gcloud redis instances create ${REDIS_NAME} \
--size 1 \
--region ${REGION} \
--zone ${ZONE} \
--redis-version redis_6_x \
--network default \
--transit-encryption-mode SERVER_AUTHENTICATION
Notes:
- You can connect to a Memorystore (redis) instance from GKE clusters that are in the same region and use the same network as your instance.
- You cannot connect to a Memorystore (redis) instance from a GKE cluster without VPC-native/IP aliasing enabled.
- In-transit encryption is only available at creation time of your Memorystore (redis) instance. The Certificate Authority is valid for 10 years, rotation every 5 years.
Wait for the Memorystore (redis) instance to be succesfully provisioned and get the connection information of the Memorystore (redis) instance just provisioned, we will need these information in a following section:
REDIS_IP=$(gcloud redis instances describe ${REDIS_NAME} \
--region ${REGION} \
--format 'get(host)')
REDIS_PORT=$(gcloud redis instances describe ${REDIS_NAME} \
--region ${REGION} \
--format 'get(port)')
gcloud redis instances describe ${REDIS_NAME} \
--region ${REGION} \
--format 'get(serverCaCerts[0].cert)' > redis-cert.pem
Connect the Online Boutique sample apps to the Memorystore (redis) instance
Update the cartservice
’s environment variables to point to the Memorystore (redis) instance:
kubectl set env deployment/cartservice REDIS_ADDR=${REDIS_IP}:${REDIS_PORT} \
-n ${NAMESPACE}
Remove the default in-cluster redis
database you won’t use anymore:
kubectl delete service redis-cart \
-n ${NAMESPACE}
kubectl delete deployment redis-cart \
-n ${NAMESPACE}
Navigate to the Online Boutique website by clicking on the link below:
echo -n "http://" && kubectl get svc frontend-external -n ${NAMESPACE} -o json | jq -r '.status.loadBalancer.ingress[0].ip'
You should have a HTTP Status: 500 Internal Server Error
page at this point. This is expected since we haven’t yet set up the TLS communication between cartservice
and the Memorystore (redis) instance. With the next sections, we will leverage ASM and Istio in order to accomplish this part.
Install the managed ASM on this cluster
Register your cluster to a Fleet:
gcloud container fleet memberships register ${CLUSTER} \
--gke-cluster ${ZONE}/${CLUSTER} \
--enable-workload-identity
Enable ASM in your Fleet:
gcloud container fleet mesh enable
Enable the ASM automatic control plane management to let Google apply the recommended configuration of ASM:
gcloud container fleet mesh update \
--control-plane automatic \
--memberships ${CLUSTER}
Configure the TLS origination from the Mesh to the Memorystore (redis) instance
Create a Secret
with the Certificate Authority generated previously:
kubectl create secret generic redis-cert \
--from-file redis-cert.pem \
-n ${NAMESPACE}
Annotate the cartservice
deployment to mount the redis-cert
secret via its istio-proxy
sidecar:
kubectl patch deployment cartservice \
-n ${NAMESPACE} \
-p '{"spec": {"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolumeMount":"[{\"name\":\"redis-cert\", \"mountPath\":\"/etc/certs\", \"readonly\":true}]"}}}} }'
kubectl patch deployment cartservice \
-n ${NAMESPACE} \
-p '{"spec": {"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolume":"[{\"name\":\"redis-cert\", \"secret\":{\"secretName\":\"redis-cert\"}}]"}}}} }'
From there we could create ServiceEntry
and DestinationRule
allowing to expose this external endpoint in the mesh with a TLS origination setup:
INTERNAL_HOST=redis.memorystore-redis
cat <<EOF | kubectl apply -n ${NAMESPACE} -f -
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: external-redis
spec:
hosts:
- ${INTERNAL_HOST}
addresses:
- ${REDIS_IP}/32
endpoints:
- address: ${REDIS_IP}
location: MESH_EXTERNAL
resolution: STATIC
ports:
- number: ${REDIS_PORT}
name: tcp-redis
protocol: TCP
EOF
cat <<EOF | kubectl apply -n ${NAMESPACE} -f -
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: external-redis
spec:
exportTo:
- '.'
host: ${INTERNAL_HOST}
trafficPolicy:
tls:
mode: SIMPLE
caCertificates: /etc/certs/redis-cert.pem
EOF
Inject the Istio/ASM sidecar proxies in the Online Boutique Namespace
:
kubectl label namespace ${NAMESPACE} istio-injection=enabled
Wait for ASM to be properly installed on your cluster. Run this command below until you see the field servicemesh.controlPlaneManagement.details[].code
with the value REVISION_READY
:
gcloud container fleet mesh describe
Restart the cartservice
app in order to inject the Istio/ASM sidecar proxies and configs we just deployed previously:
kubectl rollout restart deployment cartservice \
-n ${NAMESPACE}
Navigate to the Online Boutique website by clicking on the link below:
echo -n "http://" && kubectl get svc frontend-external -n ${NAMESPACE} -o json | jq -r '.status.loadBalancer.ingress[0].ip'
You should now see the Online Boutique website working successfully again, now connected to the Memorystore (redis) instance.
Cleaning up
To avoid incurring charges to your Google Cloud account, you can delete the resources used in this tutorial.
Unregister the GKE cluster from the Fleet:
gcloud container fleet memberships unregister ${CLUSTER} \
--project=${PROJECT_ID} \
--gke-cluster=${ZONE}/${CLUSTER}
Delete the GKE cluster:
gcloud container clusters delete ${CLUSTER} \
--zone ${ZONE}
Delete the Memorystore (redis) instance:
gcloud redis instances delete ${REDIS_NAME}
What’s next
- Watch this ASM value over Istio OSS episode on YouTube.
- Learn more about ASM security best practices.
- Watch this Architecting zero trust networks with GKE and ASM episode on YouTube
- Learn more about Strengthen your app’s security with ASM and Anthos Config Management.