Securing Istio and Kubernetes With a Private Certificate Authority
Kevin Chen
Securing Istio and Kubernetes With a Private Certificate Authority
Prior to joining Smallstep, I worked in the realm of network connectivity, with a heavy focus on service mesh. To directly quote 2019 me, “Service mesh is redefining the way we think about security, reliability, and observability when it comes to service-to-service communication.” So it felt fitting to expand on the security component for my first blog post at a security toolkit company.
This post will cover how to secure an Istio mesh running on a Kubernetes cluster with a private certificate authority (CA). But I do want to give shoutouts to some awesome open-source alternatives. LinkerD offers a unique service mesh offering for Kubernetes that utilizes their homegrown lightning-fast dataplane proxy. It is a Cloud Native Computing Foundation (CNCF) incubating project. On the subject of CNCF projects, Kuma is another project you can explore. Much like Istio, it utilizes Envoy as the sidecar proxy, but with a heavier emphasis on universal deployments.
Why Use a Private Certificate Authority
Before we dive into how to achieve this, let’s take a second to talk about why you would want to use a private certificate authority for your Istio workloads. For most it comes down to some combination of:
- You don’t completely trust your root private key in a kubernetes secret and want a cloud Key Management System (KMS) or Hardware Security Module (HSM) support.
- You want TLS inside, outside, and between Istio and kubernetes clusters.
- You manage more than containers and want automated certs for humans, machines, and devices.
There are cases where a private CA is not required. Most service meshes ship with an embedded CA. In Istio, the embedded CA is called Citadel. Citadel provides strong identities to workloads with X.509 certificates. But will all your workloads and services be in Istio? Will it all be in Kubernetes to begin with? I am willing to bet that for the majority of folks, no. Certificates extend beyond Istio workloads to Kubernetes, machine identity, cloud VMs, and across legacy environments. For diverse workloads and distributed machines that all need certificates, using step-ca
for managing said certificates is simple. You can use OIDC to connect your identity provider and issue single sign-on certificates to humans. We also support ACME for automating certificates to servers, VMs, and internal websites. By connecting your Istio implementation to the broader tech stack with step-ca
, you unlock some powerful extended use cases.
A Hardware Security Module (HSM) is a specialized device that is designed to generate, store, and use private keys securely. For folks that want to store private keys in something other than a kubernetes secret, step-ca
supports cloud KMSs and PKCS 11, the specification for HSMs. The private keys on a HSM cannot be exported from the device. One can only run signing operations on the device. This is an excellent way to protect private keys for a Certificate Authority, whose primary function is to sign Certificate Signing Requests. In the early 2000s, HSMs were sometimes marketed as “eCommerce Accelerators” because companies used them primarily to speed up cryptographic operations for SSL connections. Today, they are more often used to meet tight security and compliance requirements. To learn how to use step-ca
with HSM, please follow this blog post.
Another added benefit of step-ca
is templates. Templates enable a organization to automate certificate details and bring identity standards across the organization. Try one of the several built-in templates for everyday operations, or users can use Golang's text/template syntax to create new templates. Template are JSON documents that can be configured to support certificates for TLS, SSH, code signing, email signing, or any other required certificate format.
And working alongside cert-manager, step-ca
delivers enterprise security to Istio workloads. Cert-manager is a Kubernetes certificate management controller. This important add-on helps issue and renew certificates by monitoring Kubernetes secrets. But once we’re outside Kubernetes, step-ca
continues to deliver the same enterprise security to your remaining workloads.
Enough talk, let us jump to the terminal and get our hands dirty.
Pre-requisites
To follow this blog post, please prepare the following things ready on your personal machine:
Kubernetes Cluster - I tested this blog post on GKE and minikube. But if you want to run on OpenShift, follow these instructions to prepare an OpenShift cluster for Istio. Step-CLI - Command-line tool to configure, operate, and automate the smallstep toolchain and open standard identity technologies. istioctl - Command-line tool that allows service operators to debug and diagnose their Istio service mesh deployments. Helm - Package manager for Kubernetes to help use install cert-manager and step-issuer.
cert-manager
Jetstack's cert-manager is a Kubernetes certificate management controller. This important add-on helps issue and renew certificates.
Installation
Using Helm, run the following commands to install cert-manager:
$ helm repo add jetstack https://charts.jetstack.io
"jetstack" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.3.1 \
--set installCRDs=true
NAME: cert-manager
LAST DEPLOYED: Mon May 10 08:21:13 2021
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://cert-manager.io/docs/configuration/
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://cert-manager.io/docs/usage/ingress/
Check all the pods within the cert-manager namespace.
$ kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-7998c69865-vzgg7 1/1 Running 0 5m40s
cert-manager-cainjector-7b744d56fb-c68f4 1/1 Running 0 5m40s
cert-manager-webhook-7d6d4c78bc-4wvns 1/1 Running 0 5m40s
Step Toolkit
cert-manager supports various different external CAs. Step Issuer uses step-certificate as the Certificate Authority in charge of signing the CertificateRequest resources. So before we install our issuer, let's set up a step-certificate CA into our cluster. And if you have any questions about the toolkit or this blog post, visit the Smallstep Discord channel to find our maintainers and community members.
Step Certificate Installation
Using Helm, install step-certificates first:
$ helm repo add smallstep https://smallstep.github.io/helm-charts
"smallstep" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "smallstep" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install \
step-certificates smallstep/step-certificates \
--namespace istio-system \
--create-namespace
NAME: step-certificates
LAST DEPLOYED: Mon May 10 08:45:13 2021
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
NOTES:
Thanks for installing Step CA.
1. Get the PKI and Provisioner secrets running these commands:
kubectl get -n istio-system -o jsonpath='{.data.password}' secret/step-certificates-ca-password | base64 --decode
kubectl get -n istio-system -o jsonpath='{.data.password}' secret/step-certificates-provisioner-password | base64 --decode
2. Get the CA URL and the root certificate fingerprint running this command:
kubectl -n istio-system logs job.batch/step-certificates
3. Delete the configuration job running this command:
kubectl -n istio-system delete job.batch/step-certificates
With step-ca
installed, we need to get the base64 version of our root certificate and kid. Note that your values will be different from the ones listed on this blog post
To get the base64 version of the root certificate:
$ kubectl get -o jsonpath="{.data['root_ca\.crt']}" configmaps/step-certificates-certs -n istio-system | tr -d '\n' | base64
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpekNDQVRLZ0F3SUJBZ0lSQUpSUzVPYTR0cHdCTUdwRFF0UnRIcTB3Q2dZSUtvWkl6ajBFQXdJd0pERWkKTUNBR0ExVUVBeE1aVTNSbGNDQkRaWEowYVdacFkyRjBaWE1nVW05dmRDQkRRVEFlRncweU1UQTFNVGN4TVRRdwpORFZhRncwek1UQTFNVFV4TVRRd05EVmFNQ1F4SWpBZ0JnTlZCQU1UR1ZOMFpYQWdRMlZ5ZEdsbWFXTmhkR1Z6CklGSnZiM1FnUTBFd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFSOG9tUUpNbk1CQjcxekpsak0KRWNkYTdiU2tmck9MZjlEamw3WjN1alRjMVdOQTl2ZnBGRFdud0ErbGk5NnlpQVJ6eVFTZTk0d2VVUU55Rnhqegp1NS9BbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFUQWRCZ05WCkhRNEVGZ1FVc1R6WjV6NENjZTJRQ3FZSnhlWDM0bGVVUWJrd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1RFc0YKZkl0RThQREVnbGUvMEhWMjZCakl4RDRWOHUzZDZzNmFCbnEvMmw4Q0lEczJvbGpRcVpFMjduNXhNZFNSOS84UworbjVHWU9aanc1UEkxZEFjQ3J1QwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
To get the provisioner kid:
$ kubectl get -o jsonpath="{.data['ca\.json']}" configmaps/step-certificates-config -n istio-system | jq .authority.provisioners
[
{
"type": "JWK",
"name": "admin",
"key": {
"use": "sig",
"kty": "EC",
"kid": "E4BpJlpL8QayAvkbKJRITAvaKZgJr8w7GsajKOxB4aU",
"crv": "P-256",
"alg": "ES256",
"x": "UBx4JLlU0dZyJ5yqbCVQzS1SnaLV2Ga8U9u8Ib91A-w",
"y": "MobU8KE6c8qQn8Z2MZ28yiIYn9XfDWJv83wgdFJW0E4"
},
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiWjA3WFc2cXU2SGF1QmhzX3Q5ZlBTQSJ9.kwcZH8r0jpKdN3O_QopnStG5zeMOkWkj7P3oueN5ia6uziv2XnoSaQ.Q0B7eo3i-HgLO9zK.DTxZ_22iRLwiVZOZCMPwL974jz4qJEV2LxJc21BAsCqWLdxZ7SJd8YlrBUHNCS0ZjR9LwgHPOtE4Fn23III0PTqJO0yDmFpLrUDkeDyOVFln56es8MDlb9xyPhQGDVYJ2yFqIAz_gQqMZz8L99k2QjANF2aPC45gfy7jZDN-PYR3-u_i94BBh6ckzfrPeE5qko54CfCPftoFuBw86PRlRVDEW_e31l9sVuq-PFrUF5nb4s-YQDR2BuyyXlT5eFC79TWxmkV3zZdnbsL86Cm7zlEW_tp4uLT_m4N1BONSnOIHznrXQMmMzBdjShG7QLXomZyT4fGPj8EWjDfDieM.eojVrYdpchvYag1EYcX5Yw"
}
]
To save these values, create a issuer-config.yaml
file and populate it like so:
apiVersion: certmanager.step.sm/v1beta1
kind: StepIssuer
metadata:
name: step-issuer
namespace: istio-system
spec:
# The CA URL.
url: https://step-certificates.istio-system.svc.cluster.local
# The base64 encoded version of the CA root certificate in PEM format.
caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpekNDQVRLZ0F3SUJBZ0lSQUpSUzVPYTR0cHdCTUdwRFF0UnRIcTB3Q2dZSUtvWkl6ajBFQXdJd0pERWkKTUNBR0ExVUVBeE1aVTNSbGNDQkRaWEowYVdacFkyRjBaWE1nVW05dmRDQkRRVEFlRncweU1UQTFNVGN4TVRRdwpORFZhRncwek1UQTFNVFV4TVRRd05EVmFNQ1F4SWpBZ0JnTlZCQU1UR1ZOMFpYQWdRMlZ5ZEdsbWFXTmhkR1Z6CklGSnZiM1FnUTBFd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFSOG9tUUpNbk1CQjcxekpsak0KRWNkYTdiU2tmck9MZjlEamw3WjN1alRjMVdOQTl2ZnBGRFdud0ErbGk5NnlpQVJ6eVFTZTk0d2VVUU55Rnhqegp1NS9BbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFUQWRCZ05WCkhRNEVGZ1FVc1R6WjV6NENjZTJRQ3FZSnhlWDM0bGVVUWJrd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1RFc0YKZkl0RThQREVnbGUvMEhWMjZCakl4RDRWOHUzZDZzNmFCbnEvMmw4Q0lEczJvbGpRcVpFMjduNXhNZFNSOS84UworbjVHWU9aanc1UEkxZEFjQ3J1QwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
# The provisioner name, kid, and a reference to the provisioner password secret.
provisioner:
name: admin
kid: E4BpJlpL8QayAvkbKJRITAvaKZgJr8w7GsajKOxB4aU
passwordRef:
name: step-certificates-provisioner-password
key: password
Remember to replace the caBundle
with the base64 version of the root certificate and the kid
with the provisioner kid listed above.
Step Issuer Installation
We need to create an Issuer or ClusterIssuer resource before we can utilize the cert-manager we just deployed. These resources are necessary as they represent signing authorities and detail how certificate requests from Istio workload will be honored. Using Helm again, install the issuer that cert-manager relies on.
$ helm install \
step-issuer smallstep/step-issuer \
--namespace istio-system
NAME: step-issuer
LAST DEPLOYED: Mon May 10 09:27:13 2021
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
...
🍻 Happy signing.
Check the istio-system
namespace to verify all step components are up and running:
$ kubectl get -n istio-system all
NAME READY STATUS RESTARTS AGE
pod/step-certificates-0 1/1 Running 0 18m
pod/step-certificates-l7rhk 0/1 Completed 0 18m
pod/step-issuer-f6ffb88f6-h2bzx 2/2 Running 0 119s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/step-certificates ClusterIP 10.108.172.126 <none> 443/TCP 18m
service/step-issuer ClusterIP 10.104.92.237 <none> 8443/TCP 119s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/step-issuer 1/1 1 1 119s
NAME DESIRED CURRENT READY AGE
replicaset.apps/step-issuer-f6ffb88f6 1 1 1 119s
NAME READY AGE
statefulset.apps/step-certificates 1/1 18m
NAME COMPLETIONS DURATION AGE
job.batch/step-certificates 1/1 19s 18m
Issuer Configuration
Apply the previous issuer-config.yaml
file we created.
$ kubectl apply -f issuer-config.yaml
stepissuer.certmanager.step.sm/step-issuer created
Moments later you should be able to see the status property in the resource:
$ kubectl get stepissuers.certmanager.step.sm step-issuer -n istio-system -o yaml
apiVersion: certmanager.step.sm/v1beta1
kind: StepIssuer
...
status:
conditions:
- lastTransitionTime: "2021-05-11T12:05:45Z"
message: StepIssuer verified and ready to sign certificates
reason: Verified
status: "True"
type: Ready
CertificateRequest Creation
Step Issuer has a controller watching for CertificateRequest resources. To create this CertificateRequest we first need a CSR. We can use step to create one, we will use the password my-password
to encrypt the private key:
$ step certificate create --csr internal.smallstep.com internal.csr internal.key
Please enter the password to encrypt the private key:
Your certificate signing request has been saved in internal.csr.
Your private key has been saved in internal.key.
$ cat internal.csr
-----BEGIN CERTIFICATE REQUEST-----
MIIBEDCBtwIBADAhMR8wHQYDVQQDExZpbnRlcm5hbC5zbWFsbHN0ZXAuY29tMFkw
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUzfaIYwWAw+EAglS2zxWMBsu0bd6NzF9
Nsa2G7aCUk6vDV1FplRuxo49d7SfdzSPWCoUMYx7OHRBe5Wo2JaOmKA0MDIGCSqG
SIb3DQEJDjElMCMwIQYDVR0RBBowGIIWaW50ZXJuYWwuc21hbGxzdGVwLmNvbTAK
BggqhkjOPQQDAgNIADBFAiEAl6aBHpboyIcdwXMyG2qVkSUaLgWTre/fatA4GJx8
I2cCICnf9arQngpEnxCa0pUWNHFnu7OdNSlM+RK3XlZhcdff
-----END CERTIFICATE REQUEST-----
$ cat internal.key
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,2cf0f7b4029ed9398fd28bee85520ae6
E3ovadgxvkggvauzUuJGeYObbtQrZ2fE//lFDIPOhKcgqHt4YDSAzfV6vJhYVSNB
Ac0/ncT+A4sRx6qcjjV2xgett99ZDeRHJ9mBdxUBUcAaR3uAqjEJ0vC/LYK4MjPn
yn7cnYeEFaQZKvhohR/QjyaKrNQT5YOAf6JQlb0YlkQ=
-----END EC PRIVATE KEY-----
Encode the new CSR using base64:
$ cat internal.csr | tr -d '\n' | base64
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQkVEQ0J0d0lCQURBaE1SOHdIUVlEVlFRREV4WnBiblJsY201aGJDNXpiV0ZzYkhOMFpYQXVZMjl0TUZrdwpFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRVV6ZmFJWXdXQXcrRUFnbFMyenhXTUJzdTBiZDZOekY5Ck5zYTJHN2FDVWs2dkRWMUZwbFJ1eG80OWQ3U2ZkelNQV0NvVU1ZeDdPSFJCZTVXbzJKYU9tS0EwTURJR0NTcUcKU0liM0RRRUpEakVsTUNNd0lRWURWUjBSQkJvd0dJSVdhVzUwWlhKdVlXd3VjMjFoYkd4emRHVndMbU52YlRBSwpCZ2dxaGtqT1BRUURBZ05JQURCRkFpRUFsNmFCSHBib3lJY2R3WE15RzJxVmtTVWFMZ1dUcmUvZmF0QTRHSng4CkkyY0NJQ25mOWFyUW5ncEVueENhMHBVV05IRm51N09kTlNsTStSSzNYbFpoY2RmZgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K
Lastly, create a certificate-request.yaml
and replace the spec.request value with the base64 value of your new CSR:
apiVersion: cert-manager.io/v1
kind: CertificateRequest
metadata:
name: internal-smallstep-com
namespace: istio-system
spec:
# The base64 encoded version of the certificate request in PEM format.
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQkVEQ0J0d0lCQURBaE1SOHdIUVlEVlFRREV4WnBiblJsY201aGJDNXpiV0ZzYkhOMFpYQXVZMjl0TUZrdwpFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRVV6ZmFJWXdXQXcrRUFnbFMyenhXTUJzdTBiZDZOekY5Ck5zYTJHN2FDVWs2dkRWMUZwbFJ1eG80OWQ3U2ZkelNQV0NvVU1ZeDdPSFJCZTVXbzJKYU9tS0EwTURJR0NTcUcKU0liM0RRRUpEakVsTUNNd0lRWURWUjBSQkJvd0dJSVdhVzUwWlhKdVlXd3VjMjFoYkd4emRHVndMbU52YlRBSwpCZ2dxaGtqT1BRUURBZ05JQURCRkFpRUFsNmFCSHBib3lJY2R3WE15RzJxVmtTVWFMZ1dUcmUvZmF0QTRHSng4CkkyY0NJQ25mOWFyUW5ncEVueENhMHBVV05IRm51N09kTlNsTStSSzNYbFpoY2RmZgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K
# The duration of the certificate
duration: 24h
# If the certificate will be a CA or not.
# Step certificates won't accept a certificate request if this value is true,
# you can also omit this.
isCA: false
# A reference to the issuer in charge of signing the CSR.
issuerRef:
group: certmanager.step.sm
kind: StepIssuer
name: step-issuer
Apply it using kubectl
:
$ kubectl apply -f certificate-request.yaml
certificaterequest.cert-manager.io/internal-smallstep-com created
And moments later the bundled signed certificate with the intermediate as well as the root certificate will be available in the resource:
$ kubectl get certificaterequests.cert-manager.io internal-smallstep-com -n istio-system -o yaml
apiVersion: cert-manager.io/v1
kind: CertificateRequest
...
status:
conditions:
- lastTransitionTime: "2021-05-13T12:55:11Z"
message: Certificate request has been approved by cert-manager.io
reason: cert-manager.io
status: "True"
type: Approved
Istio CSR
Connecting cert-manager to Istio without agents and addons is a pain. Trust me, I tried. But after struggling for a week, I stumbled upon cert-manager's Istio-CSR. This agent allows for Istio workload and control plane components to be secured using cert-manager with minimal work.
Installation
First, create a istio-csr-values.yaml
file to change some of the Helm values before we deploy. Certificates facilitating mTLS, inter and intra cluster, will be signed, delivered, and renewed using the specs we define below.
certificate:
# -- Namespace to create CertificateRequests from incoming gRPC CSRs.
namespace: istio-system
# -- Issuer group name set on created CertificateRequests from incoming gRPC CSRs.
group: certmanager.step.sm
# -- Issuer kind set on created CertificateRequests from incoming gRPC CSRs.
kind: StepIssuer
# -- Issuer name set on created CertificateRequests from incoming gRPC CSRs.
name: step-issuer
# -- Maximum validity duration that can be requested for a certificate.
# istio-csr will request a duration of the smaller of this value, and that of
# the incoming gRPC CSR.
maxDuration: 24h
# -- Don't delete created CertificateRequests once they have been signed.
preserveCertificateRequests: false
Next, use Helm to install istio-csr
and include the value file we just created.
$ helm install \
cert-manager-istio-csr jetstack/cert-manager-istio-csr \
-f istio-csr-values.yaml \
--namespace cert-manager
NAME: istio-csr
LAST DEPLOYED: Mon May 10 11:45:13 2021
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
Istio
Installation
Deploy the Istio operator. You will need to have istioctl
installed to do so:
$ istioctl operator init
Installing operator controller in namespace: istio-operator using image: docker.io/istio/operator:1.9.4
Operator controller will watch namespaces: istio-system
✔ Istio operator installed
✔ Installation complete
Create a file called istio-operator-config.yaml
with these following values:
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio
namespace: istio-system
spec:
profile: "demo"
hub: gcr.io/istio-release
values:
global:
# Change certificate provider to cert-manager istio agent for istio agent
caAddress: cert-manager-istio-csr.cert-manager.svc:443
components:
pilot:
k8s:
env:
# Disable istiod CA Sever functionality
- name: ENABLE_CA_SERVER
value: "false"
overlays:
- apiVersion: apps/v1
kind: Deployment
name: istiod
patches:
# Mount istiod serving and webhook certificate from Secret mount
- path: spec.template.spec.containers.[name:discovery].args[7]
value: "--tlsCertFile=/etc/cert-manager/tls/tls.crt"
- path: spec.template.spec.containers.[name:discovery].args[8]
value: "--tlsKeyFile=/etc/cert-manager/tls/tls.key"
- path: spec.template.spec.containers.[name:discovery].args[9]
value: "--caCertFile=/etc/cert-manager/ca/root-cert.pem"
- path: spec.template.spec.containers.[name:discovery].volumeMounts[6]
value:
name: cert-manager
mountPath: "/etc/cert-manager/tls"
readOnly: true
- path: spec.template.spec.containers.[name:discovery].volumeMounts[7]
value:
name: ca-root-cert
mountPath: "/etc/cert-manager/ca"
readOnly: true
- path: spec.template.spec.volumes[6]
value:
name: cert-manager
secret:
secretName: istiod-tls
- path: spec.template.spec.volumes[7]
value:
name: ca-root-cert
configMap:
defaultMode: 420
name: istio-ca-root-cert
Apply the operator configurations. The controller deployed by the init command above will detect the IstioOperator resource and then install the Istio components we want.
$ kubectl apply -f istio-operator-config.yaml
istiooperator.install.istio.io/istio created
Bookinfo Application
The last step is to deploy an application in our service mesh. The Bookinfo application is a Istio built demo application composed of four separate microservices. For simplicity sake, we'll stick with the demo application they've built to highlight what we've accomplished.
Installation
The default Istio installation uses automatic sidecar injection. Label the default namespace with istio-injection=enabled prior to deploying the application there:
$ kubectl label namespace default istio-injection=enabled
namespace/default labeled
Next, deploy the Bookinfo application using kubectl
:
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.9/samples/bookinfo/platform/kube/bookinfo.yaml
service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
deployment.apps/reviews-v2 created
deployment.apps/reviews-v3 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created
Confirm the application is running:
$ kubectl get -n default all
NAME READY STATUS RESTARTS AGE
pod/details-v1-79f774bdb9-9mc24 2/2 Running 0 9h
pod/productpage-v1-6b746f74dc-nw7mt 2/2 Running 0 9h
pod/ratings-v1-b6994bb9-hhlfg 2/2 Running 0 9h
pod/reviews-v1-545db77b95-mkrnc 2/2 Running 0 9h
pod/reviews-v2-7bf8c9648f-wkhbm 2/2 Running 0 9h
pod/reviews-v3-84779c7bbc-468x2 2/2 Running 0 9h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/details ClusterIP 10.3.253.254 <none> 9080/TCP 9h
service/kubernetes ClusterIP 10.3.240.1 <none> 443/TCP 14h
service/productpage ClusterIP 10.3.245.225 <none> 9080/TCP 9h
service/ratings ClusterIP 10.3.250.177 <none> 9080/TCP 9h
service/reviews ClusterIP 10.3.242.91 <none> 9080/TCP 9h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/details-v1 1/1 1 1 9h
deployment.apps/productpage-v1 1/1 1 1 9h
deployment.apps/ratings-v1 1/1 1 1 9h
deployment.apps/reviews-v1 1/1 1 1 9h
deployment.apps/reviews-v2 1/1 1 1 9h
deployment.apps/reviews-v3 1/1 1 1 9h
NAME DESIRED CURRENT READY AGE
replicaset.apps/details-v1-79f774bdb9 1 1 1 9h
replicaset.apps/productpage-v1-6b746f74dc 1 1 1 9h
replicaset.apps/ratings-v1-b6994bb9 1 1 1 9h
replicaset.apps/reviews-v1-545db77b95 1 1 1 9h
replicaset.apps/reviews-v2-7bf8c9648f 1 1 1 9h
replicaset.apps/reviews-v3-84779c7bbc 1 1 1 9h
The pods all contain two containers. One of the two containers is for the Envoy sidecar proxy from the automatic sidecar injection.
Examine Certificates
Let us see what is happening behind the scene as we apply all these commands. First, you can get the Step Certificates Root CA acting as the Istio CA.
$ kubectl get cm istio-ca-root-cert -o jsonpath="{.data['root-cert\.pem']}" | step certificate inspect -
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 136737417284863388231467187453976404479 (0x66deab2c6e44407384aa4004728461ff)
Signature Algorithm: ECDSA-SHA256
Issuer: CN=Step Certificates Root CA
Validity
Not Before: May 17 23:09:24 2021 UTC
Not After : May 15 23:09:24 2031 UTC
Subject: CN=Step Certificates Root CA
...
And if we dig a little deeper, we can examine the certificates making mTLS possible in our Istio cluster. Here is the certificate in the review-v1 microservice:
$ istioctl proxy-config secret reviews-v1-545db77b95-mkrnc -n default -o json | \
jq '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain.inlineBytes' | \
sed 's/"//g' | base64 --decode | openssl x509 -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
98:24:47:8a:11:f8:18:f4:16:f9:e0:91:79:8b:60:c1
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN=Step Certificates Intermediate CA
Validity
Not Before: May 17 23:45:26 2021 GMT
Not After : May 18 23:46:26 2021 GMT
Subject: CN=spiffe://cluster.local/ns/default/sa/bookinfo-reviews
...
Conclusion
That's all for my first blog post at Smallstep! Automated external certificate management for Istio environments only took 30 minutes. Using step-ca and cert-manager, we secured istio with a private certificate authority. step-ca
delivers flexibility and unifies workloads across service mesh, kubernetes, and legacy platforms. With automations like the ACME protocol and enterprise security support for HSMs, smallstep delivers automated certificate management for DevOps. Download step-ca or try our free Certificate Manager SaaS offering and get started today.