Cloud 101CircleEventsBlog
Get 50% off the Cloud Infrastructure Security training bundle with code 'unlock50advantage'

How to Secure Kubernetes Ingress?

Published 07/13/2022

How to Secure Kubernetes Ingress?

This blog was originally published by ARMO here.

Written by Ben Hirschberg, VP R&D & Co-founder, ARMO.

Ingress aims to simplify the way you create access to your Kubernetes services by leveraging traffic routing rules that are defined during the creation of the Ingress resource. This ultimately allows you to expose HTTP and HTTPS from outside the Kubernetes cluster so you no longer need to expose each service separately—something that can be expensive and tedious as an application scales, resulting in an increase in services.

Ingress also plays a vital role in securing your Kubernetes application because you can provision TLS within the Ingress resource itself. This is crucial considering other forms of securing Kubernetes applications such as network policies, which are built on an application-centric concept that determines and secures how pods communicate with each other.

Restricting pod-to-pod networking is a step in the right direction to securing a Kubernetes application. But network policies are not synonymous with firewalls, as this method only determines whether a connection is allowed—not authentication or encryption in transport, which TLS allows and is configured in the Ingress resources.

This article will look into how we can secure Ingress resources via adding TLS to Ingress and then procuring TLS/SSL certificates.

SSL/TLS for Kubernetes Ingress

Configuring Ingress in your Kubernetes application involves configuring an Ingress controller and defining the Ingress resource. The Ingress controller handles SSL, whereas the TLS certificate references are added to the Ingress resource as a Kubernetes secret object, which is accessed by the Ingress controller and made a part of its configuration. For the purposes of this blog, we’ll use the NGINX controller.

Configuring the Controller for SSL

Let’s start by configuring the controller. This is fairly simple since we are using NGINX. We need to add the ssl_certificate_by_lua_block in our nginx.conf. This is a directive run when NGINX is about to perform the SSL handshake

ssl_certificate_by_lua_block {
    certificate.call()
}


Create a TLS-Secure Object

For this, you need to create the Kubernetes secret object that will contain the certificate-related information. It will be this secret that will be referred to in the Ingress object so you don’t need to put sensitive information in the resource definition itself.

The secret object will contain the server.crt and the server.key that we procure from the certificate authority. How we actually acquire this will be explained after we set up the TLS resource.

You have to create the secret in the namespace where you have your Kubernetes application. So, you need a secret YAML definition like the one shown below:

apiVersion: v1
kind: Secret
metadata:
  name: armo-tls-example
  namespace: dev
type: kubernetes.io/tls
data:
  server.crt: |
       <crt contents here>
  server.key: |
       <private key contents here>


The secrets file can be generated by using the following command:

kubectl create secret armo-tls-example \
    --namespace dev \
    --key server.key \
    --cert server.crt


You should specify the file paths of server.crt and server.key in the command. In the example above, you can see that we have named the secrets object armo-tls-example. We will refer to this object by this name in our Ingress resource YAML definition.

Add a TLS Definition to the Ingress Resource

Now that you’ve created the secret object, you need to leverage it in the Ingress resource YAML definition. For example, if the Ingress resource YAML file is as below:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: armo-ingress-example
spec:
  ingressClassName: nginx
  rules:
  - host: armoexample.com
    http:
      paths:
      - path: /kubernetes
        pathType: Prefix
        backend:
          service:
            name: armoservice1
            port:
              number: 80
      - path: /kubernetes/security/
        pathType: Prefix
        backend:
          service:
            name: armoservice2
            port:
              number: 80


With the TLS definition block, the YAML definition should be as follows:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: armo-ingress-example
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - armoexample.com
    secretName: armo-tls-example 
  rules:
  - host: armoexample.com
    http:
      paths:
      - path: /kubernetes
        pathType: Prefix
        backend:
          service:
            name: armoservice1
            port:
              number: 80
      - path: /kubernetes/security/
        pathType: Prefix
        backend:
          service:
            name: armoservice2
            port:
              number: 80


This adds the TLS block under the spec field of the YAML definition. The secret is referenced by the name chosen when it was created, armo-tls-example. You also need to make sure the TLS block and rules block have the same host since the TLS is being applied to the Ingress traffic per the rules defined.

Acquiring SSL Certificates

Acquiring SSL certificates requires the setup of an issuer and a certificate authority (CA). The issuer simplifies the lifecycle automation of the certificates generated by the CA by defining how to procure, renew, and utilize the certificates.

We’ll be using cert-manager, which is one of the most popular certificate issuers. For the CA, we’ll use Let’s Encrypt, also one of the more popular CA tools.

The first thing you need to do is install cert-manager in your Kubernetes cluster. This is fairly simple if you use Helm. If needed, you can refer to cert-manager’s documentation.

After verifying that you have installed cert-manager correctly, move on to creating the certificate issuer YAML definition for Let’s Encrypt. Remember, cert-manager alone cannot issue certificates. It is the CA, Let’s Encrypt in this case, that will handle the actual certificate issuance.

The certificate issuer YAML for Let’s Encrypt would be as shown below:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: armo-letsencrypt-example
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: example@ domain.com
    privateKeySecretRef:
      name: armo-letsencrypt-example
    solvers:
      - http01:
          ingress:
            class: nginx


From the YAML description above, the major things to consider are:

  • The API version used to build the resource is that of cert-manager. Check cert-manager documentation to ensure you’re using the latest stable API version.
  • The Let’s Encrypt access details are captured in the email and privateKeySecretRef fields.
  • The acme field is the issuer type. You can read more about ACME in cert-manager’s documentation.

Now that the certificate issuer is set up, you need to reference it in your Ingress resource YAML description. This can be done using annotations in the YAML file.

Considering the Ingress YAML file we used in the previous sections, the new YAML file should be the following:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: armo-ingress-example
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: armo-letsencrypt-example
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - armoexample.com
    secretName: armo-tls-example 
  rules:
  - host: armoexample.com
    http:
      paths:
      - path: /kubernetes
        pathType: Prefix
        backend:
          service:
            name: armoservice1
            port:
              number: 80
      - path: /kubernetes/security/
        pathType: Prefix
        backend:
          service:
            name: armoservice2
            port:
              number: 80


Here you have added the cert-manager.io/cluster-issuer to ensure that cert-manager detects the usage of the armo-letsencrypt-example cluster issuer, as is defined in the YAML, and procures a certificate. This will be for the name defined in the TLS hostname field that you previously added.

Adding these annotations to your Ingress resource definition should complete the steps required to provision your SSL certificates.

Conclusion

Ingress is a powerful Kubernetes resource, and being able to secure it effectively allows you to use it to its full potential. It also allows you to harden your Kubernetes application security.

This piece has covered the basics of setting up TLS in Ingress as a way to bolster security. However, there are many more complex security measures and practices you can apply. That is where solutions such as Kubescape can be called upon.

Kubescape is a Kubernetes open-source platform providing a multi-cloud K8s single pane of glass, including risk analysis, security compliance, RBAC visualizer, and image vulnerabilities scanning.

To better explore how Kubescape can help you increase the security of your Kubernetes environments and clusters, visit the Kubescape GitHub page.

Share this content on your favorite social network today!