Securing Kubernetes Ingress with Ambassador and Let's Encrypt
Tags: kubernetes, beginners, tutorial, devops
Published: 2020-06-26 22:01:29

---
title: Securing Kubernetes Ingress with Ambassador and Let's Encrypt
published: true
date: 2020-06-24 23:10:00 UTC
tags: kubernetes,beginner, tutorial,devops
canonical_url: https://learncloudnative.com/blog/2020-06-24-ssl-ambassador-lets-encrypt/
---

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#introduction)Introduction

In the [previous article on Kubernetes Ingress](https://www.learncloudnative.com/blog/2020-06-18-kubernetes-ingress/) I showed you how you can configure the Ingress resource to route traffic to different URL paths or subdomains.

In addition to routing the incoming requests or exposing service API's through a single endpoint, the ingress gateways does other tasks, such as rate limiting, SSL termination, load balancing, authentication, circuit breaking and more.

In this article I will show you how to install the Ambassador Gateway and other components to be able to obtain an SSL certificate for your application. To go through this tutorial you will need a real domain name and a cloud-provider managed Kubernetes cluster.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#what-is-ssl)What is SSL?

SSL stands for secure socket layer protocol. The SSL termination or also called SSL offloading is the process of decrypting encrypted traffic. When encrypted traffic hits the ingress controller it gets decrypted there and then passed to the backend applications. Doing SSL termination at the ingress controller level also lessens the burden on your server, as you are only doing it once at the ingress controller level and not in each application.

I will be using a cloud-managed cluster and an actual domain name to demonstrate how to set up **SSL termination**. I'll be using the Ambassador controller, [cert-manager](https://github.com/jetstack/cert-manager) for managing and issuing TLS certificates, [Let's Encrypt](https://letsencrypt.org/) as the certificate authority (CA), and [Helm](https://helm.sh) to install some of the components.

Before continuing, making sure you have installed Helm by following the instructions [here](https://helm.sh/docs/intro/install/). You can run `helm version` to make sure Helm is installed:

```
$ helm version 
version.BuildInfo{Version:"v3.2.4", GitCommit:"0ad800ef43d3b826f31a5ad8dfbb4fe05d143688", GitTreeState:"dirty", GoVersion:"go1.14.3"}
```

> Helm is a package manager for Kubernetes. Instead of dealing with individual deployments, services, configuration maps, secrets, and other Kubernetes resources, Helm packages them into "charts". A chart is a collection of different Kubernetes resource files. You can then take thee charts and version, deploy, upgrade, and manage them as a single unit.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#deploying-the-sample-application)Deploying the sample application

As a sample application, I will be using the Dog Pic website.

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dogpic-web
  labels:
    app.kubernetes.io/name: dogpic-web
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: dogpic-web
  template:
    metadata:
      labels:
        app.kubernetes.io/name: dogpic-web
    spec:
      containers:
        - name: dogpic-container
          image: learncloudnative/dogpic-service:0.1.0
          ports:
            - containerPort: 3000
---
kind: Service
apiVersion: v1
metadata:
  name: dogpic-service
  labels:
    app.kubernetes.io/name: dogpic-web
spec:
  selector:
    app.kubernetes.io/name: dogpic-web
  ports:
    - port: 3000
      name: http
```

Save the above YAML in `dogpic-app.yaml` file and use `kubectl apply -f dogpic-app.yaml` to create the deployment and service.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#deploying-cert-manager)Deploying cert-manager

We will deploy the cert-manager inside the cluster. As the name suggests, the cert-manager will deal with certificates. So, whenever we need a new certificate or we need to renew an existing certificate, the cert-manager will do that for us.

The first step is to create a namespace to deploy the cert-manager in:

```
$ kubectl create ns cert-manager 
namespace/cert-manager created
```

Next, we will add the `jetstack` Helm repository and refresh the local repository cache:

```
$ 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!⎈
```

Now we are ready to install the cert-manager. Run the following Helm command to install the cert-manager:

```
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version v0.15.1 \
  --set installCRDs=true
```

In the output, you will notice the message saying that cert-manager was deployed successfully.

Before we can use it though, we need to set up either a `ClusterIssuer` or an `Issuer` resource and configure it. This resource represents a certificate signing authority (CA) and allows cert-manager to issue certificates.

The difference between a `ClusterIssuer` and an `Issuer` is that the `ClusterIssuer` operates at the cluster level, while an `Issuer` resource works on a namespace. For example, you could configure different `Issuer` resources for each namespace. Alternatively, you could create a `ClusterIssuer` to issue certificates in any namespace.

Cert-manager supports multiple issuer types. Let's Encrypt uses the ACME protocol and therefore we will configure an ACME issuer type. These protocols support different challenge mechanisms to determine and verify domain ownership.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#challenges)Challenges

In the case of the ACME protocol, cert-manager supports two challenges to verify the domain ownership: the `HTTP-01` and `DNS-01` challenge. You can read more details about each one of these on [Let's Encrypt website](https://letsencrypt.org/docs/challenge-types/).

In short, the HTTP-01 challenge is the most common challenge type. The challenge involves a file with a token that you put in a certain location on your server. For example: `http://[my-cool-domain]/.well-known/acme-challenge/[token-file]`.

The DNS-01 challenge involves modifying a DNS record for your domain. To pass this challenge, you need to create a TXT DNS record with a specific value under the domain you want to claim. Using the DNS-01 challenge only makes sense if your domain registrar has an API that can be used to automatically update the DNS records. See the [full list of providers that integrate with the Let's Encrypt DNS validation](https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438).

I will be using the HTTP-01 challenge as it is more generic than the DNS-01 which depends on your domain registrar.

Let's deploy a `ClusterIssuer` we will be using. Make sure you replace the email with your email address:

```
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: hello@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
      selector: {}
```

Let's make sure ClusterIssuer gets created and it's ready by running `kubectl describe clusterissuer` and confirming that the ACME account was registered (i.e. the email address you provided):

```
... 
Status:
  Acme:
    Last Registered Email: hello@example.com
    Uri: https://acme-v02.api.letsencrypt.org/acme/acct/89498526
  Conditions:
    Last Transition Time: 2020-06-22T20:36:04Z
    Message: The ACME account was registered with the ACME server
    Reason: ACMEAccountRegistered
    Status: True
    Type: Ready
Events: <none>
```

Similarly, if you run `kubectl get clusterissuer` you should see the indication that the `ClusterIssuer` is ready:

```
$ kubectl get clusterissuer 
NAME READY AGE
letsencrypt-prod True 2m30s
```

Later on, once we deployed the Ingress controller and set up the DNS record on the domain, we will also create a `Certificate` resource.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#ambassador-gateway)Ambassador Gateway

To install Ambassador gateway, run the two commands below. The first one will take care of installing all CRD (custom resource definitions) and the second one installs the RBAC (Role-Based Access Control) resources and creates the Ambassador deployment.

```
$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-crds.yaml
...
$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac.yaml
```

Finally, we need to create a LoadBalancer service that exposes two ports: 80 for HTTP traffic and 443 for HTTPS.

```
apiVersion: v1
kind: Service
metadata:
  name: ambassador
spec:
  type: LoadBalancer
  ports:
    - name: http
      port: 80
      targetPort: 8080
    - name: https
      port: 443
      targetPort: 8443
  selector:
    service: ambassador
```

Save the above YAML in `ambassador-svc.yaml` file and run `kubectl apply -f ambassador-svc.yaml`.

> Deploying the above service will create a load balancer in your cloud providers' account.

If you list the services, you will notice an External IP assigned to the `ambassador` service:

```
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ambassador LoadBalancer 10.0.78.66 51.143.120.54 80:31365/TCP 97s
ambassador-admin NodePort 10.0.65.191 <none> 8877:30189/TCP 4m20s 
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 30d
```

Now that we have the External IP address you can go to the website where you registered your domain and create an A DNS record that will point the domain to the external IP. This will allow you to enter `http://[my-domain.com]` in your browser and it will resolve to the above IP address (the ingress controller inside the cluster).

I will be using my domain called `startkubernetes.com`. I will set up a subdomain `dogs.startkubernetes.com` to point to the IP address of my load balancer (e.g. `51.143.120.54`) using an A record. Regardless of where you registered your domain, you should be able to update the DNS records. Check the documentation on your domain registrars website on how to do that.

Let's set up an Ingress resource, so we can reach the Dog Pic website we deployed on the subdomain (make sure you replace the `dogs.startkubernetes.com` with your domain or subdomain name):

```
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    kubernetes.io/ingress.class: ambassador
spec:
  rules:
    - host: dogs.startkubernetes.com
      http:
        paths:
        - backend:
            serviceName: dogpic-service
            servicePort: 3000
```

With ingress deploy you can open `http://dogs.startkubernetes.com` you should see the Dog Pic website as shown below.

[![dogpic website](https://learncloudnative.com/static/1ec464e6cc1e413429ceb672ebd72efe/31493/dogpic-website.png "Dog Pic Website")](/static/1ec464e6cc1e413429ceb672ebd72efe/31493/dogpic-website.png)

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#requesting-a-certificate)Requesting a certificate

To request a new certificate you need to create a `Certificate` resource. This resource includes the issuer reference (`ClusterIssuer` we created earlier), DNS names we want to request certificates for (`dogs.startkubernetes.com`), and the Secret name where the certificate will be stored.

```
apiVersion: cert-manager.io/v1alpha2
kind: Certificate 
metadata:
  name: ambassador-certs
  namespace: default
spec:
  secretName: ambassador-certs
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - dogs.startkubernetes.com
```

Make sure you replace the `dogs.startkubernetes.com` with your domain name. Once you've done that, save the YAML in `cert.yaml` and create the certificate using `kubectl apply -f -cert.yaml`.

If you list the pods, you will notice a new pod called `cm-acme-http-solver`:

```
$ kubectl get po
NAME READY STATUS RESTARTS AGE 
ambassador-9db7b5d76-jlcdg 1/1 Running 0 22h
ambassador-9db7b5d76-qcwgk 1/1 Running 0 22h
ambassador-9db7b5d76-xsfw4 1/1 Running 0 22h
cm-acme-http-solver-qzh6l 1/1 Running 0 25m
dogpic-web-7bf547bd54-f2pff 1/1 Running 0 22h
```

Cert-manager created this pod to serve the token file as explained in the Challenges section and verify the domain name.

You can also look at the logs from the pod to see the values pod expects for the challenge:

```
$ kubectl logs cm-acme-http-solver-qzh6l
I0622 20:39:26.712391 1 solver.go:39] cert-manager/acmesolver "msg"="starting listener" "expected_domain"="dogs.startkubernetes.com" "expected_key"="iqUZlG9v1K8czpAKaTpLfL278piwf-mN4VZNvuwD0Ks.xonKHFvEQg2Ox_mI0cPM7UpCUHfu6H4aKtRcdrpiLik" "expected_token"="iqUZlG9v1K8czpAKaTpLfL278piwf-mN4VZNvuwD0Ks" "listen_port"=8089
```

However, this pod is not exposed, so there's no way for Let's Encrypt to access it and do the challenge. So we need to expose this pod through an ingress. This involves creating a Kubernetes Service that points to the pod and updating the ingress. To update the ingress we will use the `Mapping` resource from Ambassador. This resource defines a mapping to redirect requests with prefix `./well-known/acme-challenge` to the Kubernetes service that goes to the pod.

```
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name: challenge-mapping
spec:
  prefix: /.well-known/acme-challenge/
  rewrite: ""
  service: challenge-service
---
apiVersion: v1
kind: Service
metadata:
  name: challenge-service
spec:
  ports:
  - port: 80
    targetPort: 8089
  selector:
    acme.cert-manager.io/http01-solver: "true"
```

Store the above in `challenge.yaml` and deploy it using `kubectl apply -f challenge.yaml`. The cert-manager will retry the challenge and issue the certificate.

You can run `kubectl get cert` and confirm the `READY` column shows `True`, like this:

```
$ kubectl get cert
NAME READY SECRET AGE 
ambassador-certs True ambassador-certs 35m
```

Here are the steps we followed to request a certificate and a figure to visualize the process.

1. Request the certificate by creating the `Certificate` resource.
2. Cert-manager creates the `http-solver` pod (exposed through the `challenge-service` we created)
3. Cert-manager uses the issuer referenced in the `Certificate` and requests the certificates for the `dnsNames` from the authority (Let's Encrypt)
4. The authority sends the challenge for the `http-solver` to prove that we own the domains and checks that the challenges are solved (i.e. downloads the file from `/.well-known/acme-challenge/`)
5. Issued certificate and key are stored in the secret, referenced by the Issuer resource

[![cert request process](https://learncloudnative.com/static/175f007e3998154ce81a0f764f19203d/914ae/cert-request-process.png "Requesting Certificates")](/static/175f007e3998154ce81a0f764f19203d/5bd27/cert-request-process.png)

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#configuring-tls-in-ingress)Configuring TLS in Ingress

To secure an Ingress we have to specify a Secret that contains the certificate and the private key. We defined the `ambassador-certs` secret name in the `Certificate` resource we created earlier.

```
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    kubernetes.io/ingress.class: ambassador
spec:
  tls:
  - hosts:
      - dogs.startkubernetes.com
    secretName: ambassador-certs
  rules:
    - host: dogs.startkubernetes.com
      http:
        paths:
        - path: /
          backend:
            serviceName: dogpic-service
            servicePort: 3000
```

Under resource specification (`spec`), we use the `tls` key to specify the hosts and the secret name where the certificate and private key are stored.

Save the above YAML in `ingress-tls.yaml` and apply it with `kubectl apply -f ingress-tls.yaml`.

If you navigate to your domain using https (e.g. `https://dogs.startkubernetes.com`) you will see that the connection is secure and it is using a valid certificate from Let's Encrypt.

[![dogpic https](https://learncloudnative.com/static/c37146eb43cf01c5c4133a4b393dd521/914ae/dogpic-https.png "dogpic https")](/static/c37146eb43cf01c5c4133a4b393dd521/146da/dogpic-https.png)

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#cleanup)Cleanup

Use the commands below to remove the everything you installed in this section:

```
kubectl delete cert ambassador-certs 
kubectl delete secret ambassador-certs 
kubectl delete -f https://www.getambassador.io/yaml/ambassador/ambassador-crds.yaml 
kubectl delete -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac.yaml
kubectl delete svc ambassador
helm uninstall cert-manager -n cert-manager
kubectl delete svc dogpic-service challenge-service
kubectl delete deploy dogpic-web
kubectl delete ing my-ingress
```

# [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#conclusion)Conclusion

In this article you learned how to protect your Ingress gateway. We've done that using the Ambassador gateway, however the same process could be followed for any other Kubernetes ingress controller.

**Which ingress controller are you running in your Kubernetes cluster?** You can Leave a comment below or send me a [Tweet](https://twitter.com/pjausovec)!

If you liked this article you will definitely like my new course called [Start Kubernetes](https://startkubernetes.com). This course includes everything I know about Kubernetes in an ebook, set of videos and practial labs.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#questions-and-comments)Questions and Comments

I am always eager to hear your questions and comments. You can reach me on [Twitter](https://twitter.com/pjausovec) or leave a comment or question under this article.

If you are interested in more articles and topics like this one, make sure you sign up for my [newsletter](https://www.learncloudnative.com/newsletter).