Most of the time when we expose
our application to the internet, we want it to be secure. One way we can
protect it is by encrypting all traffic between the client and the application itself.
This would help prevent man-in-the-middle attacks, where an unauthorized entity can intercept every
single packet that you send to the server. For example, if it's an ecommerce website, a hacker
can steal your credit card number if you enter it in the online form to pay for goods. Also, if you
enter the username and password for social media, this information can also be stolen. Now, when
we encrypt the traffic, even if a hacker can still listen to all the communications between
us and the application, they won’t be able to decrypt that data such as credit card
information or a username and a password. To encrypt the traffic, we need to get a
certificate from a well-known trusted certificate authority. And the best part is it’s free if
you use Let's Encrypt. The key part here is "well-known," because you can generate your
own certificate authority and issue yourself a certificate, but no one in the world would trust
it. The trust is established by installing a root certificate from the CA, such as Let's Encrypt.
Since most of the computers and mobile devices are shipped with pre-installed root certificates,
you don’t need to worry about that part. When a client first visits a website
protected by TLS, the client and the server perform a TLS handshake. During the
TLS handshake, they generate session keys, and those keys are used to encrypt and
decrypt all communications after that. Now, to issue a certificate, first you need
to generate a private key and a certificate request. Then you would send the certificate
request to a certificate authority such as Let's Encrypt to have it signed. And of course,
you would put a domain name or multiple ones in the certificate request that you want to
use, such as devopsbyexample.com. Next, the certificate authority would ask you to prove
that you actually own that domain. You usually have three options. You can choose to verify it
using email, which is very hard to automate and isn’t used in Kubernetes. Another more common
options are the HTTP-01 and DNS-01 challenges. First, let's talk about the HTTP-01 challenge.
This is a generic challenge that is not unique to Let's Encrypt. When you request a certificate,
Let's Encrypt will give you a token which you need to expose on your website by following
a specific pattern. Let's Encrypt would try to retrieve that token back from you multiple
times, and if it succeeds, it will sign your certificate request and issue a certificate. Keep
in mind the certificate is public and everyone can have a copy of it, but you must keep your
private key secure and not give it to anyone. Second, a common challenge is DNS-01. Let's
Encrypt will give you the same token, but you will need to create a TXT DNS record to prove that
you control your domain. When Let's Encrypt is able to query DNS and retrieve that token, they
will issue a certificate. Later in the video, we’ll discuss which challenge you should use in
production and why. It’s not only my preference but it’s based on real-world experience. And in
the second part, we’ll run two demos, and I’ll show you how to use both challenges, which are
applicable in all clouds: AWS, GCP, and Azure. To automate this process, there are a few clients
that can help you depending on your use case. If you want to get certificates for Kubernetes,
most of the time you would use cert-manager. No matter which challenge you use, HTTP-01
or DNS-01, cert-manager internally follows the same process. When you create an ingress
and specify the TLS section, cert-manager will create a Certificate custom resource and
generate a private key that will be stored in the Kubernetes secret. Then, cert-manager
will create a CertificateRequest custom resource, and in this case, the certificate request will
be stored in the custom resource itself since it’s public. The CertificateRequest will create
an order, and finally, the order will create a challenge. The challenge can be either HTTP-01
or DNS-01 type. When the challenge is resolved, cert-manager will receive a public certificate
and store it in the same Kubernetes secret with the private key. That certificate will be valid
for 90 days, and approximately every 60 days, cert-manager will automatically renew
it by following the same process. When you register with Let's
Encrypt, you use your email address, and if for some reason cert-manager fails
to renew it, you’ll get an email warning. You can also use Prometheus and create
an alert based on the expiration date. Now when you get the certificate, the
encryption and decryption are performed at the ingress controller level, and all
traffic inside your Kubernetes cluster will stay unencrypted. There are options to encrypt
absolutely all traffic, but in most cases, it’s not necessary, and since encryption is
a CPU-intensive task, it will require you to allocate more resources to your applications,
and you may see an increase in latency as well. Alright, let’s go ahead and run the demo. The
first http-01 challenge is much easier to set up, and you’ll see why soon. First of all, we
need to create an issuer or cluster issuer. I prefer clusterIssuer because you can create
it once and use it in all namespaces. Then, you MUST specify your email address. If, for some
reason, cert-manager fails to renew your cert, you’ll get an email message. Then we
can use the production environment, or if you have never used it, you can
try the staging environment. By the way, I have a detailed tutorial on how to set
everything up. Then it’s very important for this issuer to properly set up ingressClassName.
It must match the same ingress class name that you used when you deployed the nginx ingress
controller. Previously, we had to use additional ingress annotations to resolve this
challenge, but newer versions of cert-managers support the ingressClassName property.
Then we have a simple deployment object. Take note of the HTTP variable that
we will reference in the service. Next, we have a service and use that HTTP
variable as the targetPort. It’s optional, but I still prefer this approach; you
can just drop the targetPort altogether. And for the ingress, we must specify the cluster
issuer that we created earlier, the host that you want to use, and you must match the service
name and the port number to make it work. And of course, we need to define TLS section
if we want to secure it with a TLS certificate. So, I have Terraform code that you can
use to create an EKS cluster in AWS, but the Terraform code for cert-manager and
ingress controller is generic and can be used in other clouds. You just need to use proper
annotations to create layer 4 load balancers. In most clouds, it’s the default, but in AWS,
to get it instead of a classic load balancer, you have to explicitly define it. You
can find the source code in my public GitHub repo; the link is in the description.
Alright, let’s go ahead and apply this first example. As soon as you create ingress with TLS
section, cert-manager will immediately create a certificate custom resource placeholder. As
you can see, initially, it won’t be ready. Now, cert-manager at the same time generates a private
key, and it will be stored in a native Kubernetes secret. That Certificate custom resource
will simply have a reference to that secret. Every Kubernetes secret is encoded in base64,
so if you want to get the private key for some reason, maybe to test something, you can get the
value using the YAML output option. Then you can just use the base64 Linux utility and decode
it. Keep in mind, base64 is not encryption; it’s just a common encoding technique. And
this is actually the private key itself. Now, let me show you how to debug if the
certificate remains in the 'Non-ready' state. Well, as with any other custom resource, you
can describe it and get related events. This is the best place to start. If you have multiple
certificates, you would add the name of the certificate, such as app-devopsbyexample-com,
at the end of this command. Now you can see that the CertificateRequest custom
resource was created. And you can also notice the reference to the private key Kubernetes secret.
Well, let’s go ahead and describe that CertificateRequest. In this case, you can
actually find the certificate request itself as a part of the custom resource. Well, because it’s
public, and you can safely share it with anyone. We can use the same approach if you want, and
decode that request to the regular string from base64. Alright, this is the certificate request.
Now let me describe that request one more time. And you can see the event
that an Order was created. Alright, let’s go ahead and describe it
as well. And finally, you can find that the challenge was created. Let’s describe
that challenge. Here you can actually find the reason why the certificate remains
in the pending state. Well, obviously, we need to create a DNS record and point
it to the load balancer. You don’t need to specifically create something to resolve
that challenge; this DNS is needed in either case just to route traffic to your application in
Kubernetes. You can install an additional addon, external-dns, which can automatically create
DNS, but it’s out of scope for this tutorial. Alright, let’s go ahead and
create a DNS record. In AWS, you need to create a CNAME or an
alias if it’s managed by Route53. Before that, let me show you that cert-manager
actually created an additional ingress in Kubernetes to resolve the challenge. Also,
it has a pod and a service. This pod simply returns the token that LetsEncrypt
gave it to prove that you control your domain. When the challenge is resolved,
all those additional objects—ingress, pod, and service—will be deleted and only recreated
in about 60 days to renew your certificate. Let’s go ahead and create the CNAME DNS
record to make our ingress work. In AWS, you would use the load balancer
hostname as a value; in GCP and Azure, you’ll get an IP address and you would
need to create an A record instead. Now let’s wait until the challenge is resolved.
In my case, it took a couple of minutes, but it’s possible to take even a few hours; just
try to resolve your domain yourself to verify. If the certificate remains in a non-ready state, just
describe the challenge again, and you’ll see why. You can see that the temporary ingress was
removed, as well as the service and the pod. Finally, let’s check in the browser if we can
access your application using HTTPS. Alright, it works, and you can get a certificate
to check the issuer and expiration date. And the challenge will be removed as well. Let’s clean up and delete the first
example. To automate the DNS-01 challenge, you need to integrate your cert-manager with
a cloud DNS provider. It works with AWS, GCP, Azure, and many others; you just need
to provide credentials. In my case, I used an OpenID Connect provider and
created a dedicated IAM role to be used by the cert-manager Kubernetes service
account. It’s all in the Terraform code. I already have a public hosted
zone with a few records. For this issuer, you need to provide the resolver; in this case, it’s Route 53. You also need
to specify the region, hosted zone ID, as well as the hosted zone domain. Other than
that, the remaining components stay the same. In Terraform, you can find how to configure
it to resolve the DNS-01 challenge. You need to provide the service account name and
the IAM role ARN. If you make a mistake, cert-manager will complain that it does not
have permissions to create DNS TXT records. And this is the role itself with the
necessary permissions to create Route 53 DNS records. You can improve it a little
by limiting access to only a specific hosted zone. And also keep in mind that cert-manager
must be deployed in the cert-manager namespace. Alright, let’s go ahead and apply it.
Now we see the same certificate. And if we describe the challenge, you’ll see the
DNS-01 type instead of HTTP-01. In Route 53, you can find the TXT record that was created by
cert-manager to prove ownership. In a few minutes, you should get a certificate. Also,
let's create a CNAME record for our ingress. You can automate this
with the external-dns component. Let’s make sure that DNS is
ready. And from the browser, you can verify that the certificate is valid. Now this approach to resolve the DNS-01
challenge requires more configurations but is preferred in production. For example, in
the case of disaster recovery or if you simply migrate your applications from one Kubernetes
cluster to another, with the HTTP-01 challenge, it would take a few minutes to obtain a
certificate, which means your clients won’t be able to connect to your application,
and it will cause downtime. With DNS-01, you can obtain a certificate before you start
shifting traffic to the new location. You can start with HTTP-01; it’s not a big deal,
and when you need to migrate your app, just configure the DNS resolver. Thank you for
watching, and I’ll see you in the next video.