Kubernetes: Using Ingress with SSL/TLS termination and HTTP/2

tl;dr: Ingress is a concept in Kubernetes to route inbound connections to different services by hostnames and paths. With Ingress you can also secure your connections with SSL/TLS termination and on top enable HTTP/2 features for applications.

At first you have to understand the concept of Ingress. You have to know how to install an Ingress controller in a Kubernetes cluster. And last but not least you have to learn how to write Ingress resources. All this is covered in this article.

For details on Kubernetes in general see Recommended reading: Kubernetes in Action.

Starting with Ingress

Ingress is a concept in Kubernetes to route inbound connections to different services. This can be achieved in different ways.

One way is to create name-based virtual hosting with Ingress. With that you can define routes to different services inside of your Kubernetes cluster, depending on the incoming requests' hostnames. This allows you to run multiple services on the same IP address.

Another way is to create a path-based Ingress. With a path-based Ingress you can route specific paths to specific services. On top of that you also get the possibility to load balance the inbound connections to different services, depending on their paths, e.g. to implement API versioning.

Last but not least you can also enable SSL/TLS termination for each of your Ingress resources.

At the beginning you need an Ingress controller up and running inside of your Kubernetes cluster. Its job is to manage Ingress resources. Ingress resources are state descriptions that define how to route connections and also how to enable SSL/TLS termination for them. In contrast, the Ingress controller is about representing and managing these state descriptions.

All of the following examples will be implemented with the widely used NGINX Ingress controller.

For installing ready-made services inside of a Kubernetes cluster you can use Helm, the package manager for Kubernetes. In Helm there are already many recipes (the so-called charts) available for different services, among others the nginx-ingress chart. If Helm is already installed then you can simply install the Ingress controller with the following command:

$ helm install --version 0.18.1 stable/ingress-nginx

After the installation of nginx-ingress you can now create and apply Ingress resources to the Kubernetes cluster. A name-based Ingress resource basically looks like this:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: service1
          servicePort: 80
  - host: foo.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: service2
          servicePort: 80

If instead you want to use path-based routing, you also get the possibility to load balance inbound connections. For that, use the following state description:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /v1
        backend:
          serviceName: service1
          servicePort: 80
      - path: /v2
        backend:
          serviceName: service2
          servicePort: 80

Using SSL/TLS termination

For security and privacy reasons you may want to use SSL/TLS termination, e.g. to protect against MITM attacks. This is required if you transfer sensitive user data, such as credentials or personal data. Additionally, if you want to use HTTP/2 features such as multiplexing or data streaming, then SSL/TLS termination is required as well.

The most common way to secure Ingress resources with SSL/TLS termination is on the proxy level, which means the Ingress controller. This is mostly used for websites. The following example shows how to do this with a manually created secret that contains a private key and a certificate:

apiVersion: v1
kind: Secret
metadata:
  name: testsecret
  namespace: default
data:
  tls.crt: <base64_encoded_cert>
  tls.key: <base64_encoded_key>
type: Opaque
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  tls:
  - hosts:
    - example.com
    secretName: testsecret
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: service1
          servicePort: 80

Maybe you don't want to create secrets manually. Therefore you can use external services such as Let's Encrypt. There is also a great ready-made implementation available as a Helm chart. For details see the cert-manager chart.

Passing through HTTPS and HTTP/2 with Ingress

With the SSL/TLS example from above there is a problem if you want to use HTTP/2 features. This is because HTTP/2 requires an end-to-end HTTPS connection, but internally there is only an HTTP connection to the specified backend given. Even if the proxy used HTTPS internally then you still wouldn't have an end-to-end encryption, which also means that the HTTP/2 features could not be used.

If you need to use HTTP/2 features for your application, you have to pass through the HTTPS connection directly to your backend. The same is true if you have already implemented SSL/TLS termination in your backend.

For these cases the Ingress controller has an option to enable SSL/TLS pass through. At first you have to install the Ingress controller with a specific parameter, to make SSL/TLS pass through available as a feature (if an Ingress controller is already installed, you have to remove it first):

$ helm install \
    --version 0.18.1 \
    --set controller.extraArgs.enable-ssl-passthrough="" \
    stable/ingress-nginx

After having installed the Ingress controller with the SSL/TLS pass through feature, you have to extend the Ingress resource with an annotation that enables SSL/TLS pass through for it. The whole magic is then done by the Ingress controller. The following example shows how to enable SSL/TLS pass through for an Ingress resource:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: service1
          servicePort: 443

At this point it also makes sense to disable HTTP connections in general, because by default it is possible to reach the backend also on HTTP. To do this you have to add the force-ssl-redirect annotation:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: service1
          servicePort: 443

Now, finally, you have a way of running multiple services on a Kubernetes cluster side-by-side, with routing options by hostname or by path, and with SSL/TLS integration and even HTTP/2 support.

Twitter Facebook LinkedIn

Jan-Hendrik Grundhöfer

Core development

Since everybody is deeply rooted somewhere, we want you to live wherever you want. Although we have various offices, any place with a cozy desk, a cellular network and web access is fine. On earth and beyond.