Istio with HTTPS Traffic: Secure your Service Mesh One Step at a Time
TL;DR
We are going to see how we can setup SSL certificate with Istio Gateway. We are not going to use any additional Kubernetes Ingress. We will setup SSL certificate for the Istio-IngressGateway LoadBalancer Service that Istio gives you out of the box.
We will setup SSL Certificate in two different ways.
Cert-Bot
This approach is a bit of a manual and you have to manually renew the certificate after it’s expired. But what I like about it is, it’s certificate validation step is instantaneous.
Cert-Manager
Using Cert-Manager(an open-source application that creates and renews SSL Certificates automatically in Kubernetes environments) for Dev and Staging environment.
Purchased SSL Certificate
If you have purchased an SSL certificate from a Certificate Authority(CA), you can use this approach
Steps We Will Follow
Common Steps
Step 1: Install GKE Cluster
Step 2: Install Istio
Step 3: Setup Demo App
Step 4: Reserve a Static IP
Step 5: Update Istio-IngressGateway LoadBalancer IP Address
Step 6: DNS Mapping
Cert Bot
Step 7: Generate the ACME Challenge TXTStep
Step 8: Generate the .crt and .key files
Cert-Manager
Step 9: Install Cert-Manager
Step10: Setup ClusterIssuer
Step 11: Create Certificate
Step 12: Update Gateway
Step 13: Redirect HTTP traffic
File Mount
Step 14: Prepare .crt file for Creating Secret
Step 15: Create a Secret with the .key and .crt Files
Step 16: Update Production Gateway with the Secret
Step 1: Istall GKE Cluster
If you are using the GKE Console or Terraform to create your GKE cluster then make sure it meets the following prerequisites
Prerequisites
- Setup a GKE cluster with 3 n1-standard-2 nodes with auto scale enabled. If you create a basic GKE cluster with just 3 n1-standard-1 nodes, then sometime it gives OutOfCPU error as Istio itself uses up some CPU. Follow this link to get a better understanding. https://istio.io/docs/ops/deployment/performance-and-scalability
- Check if your cluster is private cluster or it’s protected by firewall rules. Just connect to your cluster using gcloud CLI and run kubectl get pods If you get a Timeout error then use a VPN or Whitelist your IP address so you can access the cluster using kubectl. By following this guide https://istio.io/docs/setup/platform-setup/gke, you need to open the port 10250, 15017 by updating the firewall rule.
gcloud compute firewall-rules list - filter="name~gke-<CLUSTER_NAME>-[0–9a-z]*-master"
gcloud compute firewall-rules update <FIREWALL_RULE_NAME> — allow tcp:10250,tcp:443,tcp:15017
Note: If the cluster is not private, then you don’t need to go through these previous steps.
- The YAML manifest files that I am going to use for Cert-Manager will use the version v0.15. This version needs Kubernetes 1.15+. So if you are following along, then make sure to setup a Kubernetes cluster with a version 1.15+.
If you are using the gcloud CLI, then use this command
Step 2: Install Istio(Version < 1.7)
Use the following command to install Istio
istioctl manifest generate — set profile=demo > istio.yamlkubectl apply -f istio.yaml
Note: Demo profile is not optimised for production. But it helps you explore what istio is capable of.
Step 3: Setup Demo App
We will setup a demo application from the Istio GitHub repository sample applications. For our case Hello World app is good enough.
Now we have to create a Gateway to specify a Port and Protocol to allow the traffic to come in. And also create a VirtualService to tell Istio how to forward the traffic from which Gateway to which Kubernetes Service.
Step 4: Reserve a Static IP
Reserve a Static IP Address to point your domain name. Why? Because the IP Address that is attached to your istio-ingressgateway
LoadBalancer is ephemeral(means temporary). If for some reason you delete this LoadBalancer, this IP will be deleted as well. Then you have to do the domain name mapping all over again. And it takes some time to propagate the DNS as well.
If you reserve a Static IP address, it will stay reserved for you even if you delete the LoadBalancer that was using it.
Important Note
Do not create a Global IP. Use a Regional IP Address. Istio does not use Ingress. It uses a feature rich LoadBalancer as an alternative to Ingress. And Global Static IP can not be pointed to LoadBalancers.
export ADDRESS_NAME=my-ip-address
export COMPUTE_ZONE=us-central1 gcloud compute addresses create $ADDRESS_NAME \ --region $REGION
Enter the following command to get the newly created static IP address
gcloud compute addresses list
Step 5: Update Istio-IngressGateway LoadBalancer IP Address
View the current istio-ingressgateway IP
INGRESSGATEWAY=istio-ingressgateway kubectl get svc $INGRESSGATEWAY --namespace istio-system
Update the IP with your reserved IP address
# Replace the <RESERVER_IP> with your reserved IP address manually in the following command
kubectl patch svc $INGRESSGATEWAY --namespace istio-system --patch '{"spec": { "loadBalancerIP": "<RESERVER_IP>" }}'
Check if the IP has been updated properly
kubectl get svc $INGRESSGATEWAY --namespace istio-system
Step 6: DNS Mapping
You need to go to your DNS provider and create an A Record to map the domain name to the reserved IP address. After you add the A Record, go to the browser and type in your domain name in the address bar to validate if the domain name mapping has worked properly.
If it works properly, you should see a containing the pod name and version name of the Hello World application we just deployed. If you refresh the browser several times, you should see the pod name and version name changing to indicate the round robin load balancing done by Istio.
Now you need to decide how you want to setup SSL for your Istio. We have three options
- Using Cert-Bot
- Using Cert-Manager
- Using Purchased SSL
Using Cert-Bot
This is a quick but not so cool way to set up SSL certificate for any LoadBalancer or Ingress that you may be working with. It’s manual and when the certificate expires, you have to manually renew it. But the one cool thing about it is, it just works. BAAM! It’s fast, it’s instantaneous. I learned this very recently from one of my colleagues and wanted to keep a small documentation of the steps to follow for my future reference.
Step 7: Generate the ACME Challenge TXT
export YOUR_EMAIL=my_email@my_domain.com
export DOMAIN_NAME=my_domain.comsudo certbot certonly --manual --preferred-challenges=dns --email ${YOUR_EMAIL} --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d ${DOMAIN_NAME}
Once you run the command, you will be prompted for password since we have to run the command with sudo. When it asks you the question
Are you OK with your IP being logged?
Select whichever is preferable to you. When it says
Press Enter to Continue
DO NOT press enter. If you look closely, the command has provided you with two pieces of information.
You first have to create a DNS record with the _acme-challenge subdomain with the TYPE TXT and value marked in the Yellow box described in the image above.
Step 8: Generate the .crt and .key files
After you have finished creating the DNS record, press Enter in the terminal. Then Cert-Bot will validate that if you truly own the domain name my-domain.com
by looking for the TXT record we created in the previous step. If we created the record properly, then it will validate and give you the path to the files where the .crt
and .key
files are stored. You just have to create a Kubernetes Secret with these files and refer them inside the Istio Gateway. For that you can follow Step 13 and Step 14
Using Cert-Manager
Step 9: Install Cert-Manager
We are using GKE and Kubernetes version 1.15+. So just execute the following commands.
Create a cluster role binding
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=$(gcloud config get-value core/account)
Install Cert-Manager
# Kubernetes 1.15+ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.0/cert-manager.yaml
Follow the docs for more details Cert-Manager Installation guide for Kubernetes
Step 10: Setup ClusterIssuer
Create a ClusterIssuer. Cluster Issuer is cluster scoped. Not namespace specific. Just replace the email address
Step 11: Create Certificate
Important Note
You must create the Cert-Manager Certificate on the same namespace as your Istio Gateway. Because Cert-Manager Certificate obtain the SSL Certificate(SSL Certificate is different than Cert-Manager Certificate. SSL Certificate is used for encrypting web traffic.) and private key file from Let’s Encrypt and stores it in a Kubernetes Secret. The secret is created in the same namespace as that of the Certificate that you will create below. If your Gateway is in a separate namespace, then it can not read that secret.
The cert secret needs to be in the same namespace as the istio-ingressgateway which by default is in the istio-system namespace
Replace the <MAPPED_DOMAIN_NAME>
After creating the certificate, you can see what is the status of the certificate using the following command
kubectl describe certificate ingress-cert -n istio-system
You can also run the following command to get an understanding of what’s happening inside the GKE cluster in the istio-system
namespace
kubectl get events -n istio-system
Run the command after a few minutes again. You should see a that a log entry saying it created a Secret. After the Secret has been created, you need to update your Gateway to specify the name of the Secret.
Step 12: Update Gateway
When we setup our Demo Application, we created a Gateway with the following configuration.
We need to update this Gateway configuration to enable SSL.
We added new port, protocol, secret name where the SSL certificate credentials will be stored. If everything is set properly, then going to https://<DOMAIN_NAME> will work.
Step 13: Redirect HTTP traffic
If you need to redirect HTTP traffic to HTTPS, you just need to update the Gateway file
Using Purchased SSL
When you are going for Production, you need to have a purchased SSL Certificate which you can get from any Certificate Authority.
When you buy an SSL certificate, you will generally get two types of files.
- DOMAIN-NAME.key: One secret key file with the extension .key
- DOMAIN-NAME.crt: One or multiple files with the extension .crt
If you get more than one .crt files, then one of them is Root Certificate and one of them is Validation Certificate. You need to identify which one is which. If you are unsure, just ask your Certificate Provider that you purchased it from.
Step 14: Prepare .crt file for Creating Secret
After you have figured out which one is which, you need to combine the Certificate files into one with the following command
cat DOMAIN-NAME.crt ROOT-CERTIFICATE.crt > combined.crt
Or you can simply copy the content of ROOT-CERTIFICATE.crt
and paste it just below DOMAIN-NAME.crt
file. That works too.
Step 15: Create a Secret with the .key and .crt Files
Create a Secret using the combined.crt and the key files. The secret has to be created in the same namespace as your Gateway
export NAMESPACE=default
export SECRET_NAME=ingress-cert
kubectl create -n ${NAMESPACE} secret generic ${SECRET_NAME} \
-from-file=key=DOMAIN-NAME.key \
-from-file=cert=combined.crt
Specify the name of the secret name $SECRET_NAME in your Gateway YAML file
Step 16: Update Production Gateway with the Secret
This step is exactly identical to Step 11. You can use the same Gateway YAML file in production as well.
If everything is set properly, then going to https:<DOMAIN_NAME>
will work.
Important Links
How to set up HTTPS with Istio and Kubernetes on Google Kubernetes Engine
Understanding Istio Ingress Gateway in Kubernetes
Istio + cert-manager + Let’s Encrypt demystified
Istio Ingress vs. Kubernetes Ingress
https://cert-manager.io/docs/configuration/acme
https://preliminary.istio.io/latest/docs/ops/integrations/certmanager