1

I'm trying to get https working on express.js for node inside K8s deployment, but I can't figure out how.

The express app should work like this:

  1. When you access the /healthz endpoint, return 200 (both http & https requests)
  2. Redirect any other http trafic to https

The problem I'm facing is that the app is then listening on two different ports (8080 and 8443), but the K8s Ingress points to only single port - I was thinking about maybe solving this problem with Nginx as a reverse proxy, but than, how would I "link" the two containers together?

This is my app.ts:

import express from "express"
import { createServer as httpServer } from "http"
import { createServer as httpsServer } from "https"
import { readFileSync } from "fs"

const app = express()

const options = {
  key: readFileSync(`${__dirname}/cert/[company].key`),
  cert: readFileSync(`${__dirname}/cert/[company].crt`),
}

app.use("/healthz", (req, res) => {
  res.status(200).send("healthy")
})
app.use("*", (req, res) => {
  if (!req.secure) {
    return res.redirect(["https://", req.get("Host"), req.baseUrl].join(""));
  }
})
app.get("/", (req, res) => {
  res.status(200).send("Hello from Express!")
})

httpServer(app).listen(8080)
httpsServer(options, app).listen(8443)

Here's the Dockerfile:

FROM node:14.4.0-alpine
...
EXPOSE 8080 8443
CMD [ "yarn", "start" ]

Here's the Ingress.yaml:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
...
spec:
  tls:
    - secretName: [secret-name]
  rules:
    - host: [company]
      http:
        paths:
          - backend:
              serviceName: [service-name]
              servicePort: 8080

Could you please point me the right direction, how to approach/solve this problem?

Rico
  • 58,485
  • 12
  • 111
  • 141
Esskimo
  • 29
  • 6

1 Answers1

3

Answer

On Kubernetes, your Node.js server does not need to handle TLS. The canonical approach is to use Ingress for TLS termination. This is because your Node.js server can sit behind a ClusterIP Service and only be accessible from outside of the cluster through the Ingress Controller Service.

Assuming your TLS secret is valid, and your Ingress Controller is set up correctly (e.g. nginx), the YAML you have linked should work.

Make sure that the Service defined in the Ingress backend is of type ClusterIP, because it need only be accessed by the Ingress Controller Pod(s).

You would need to update your Node.js server to serve HTTP on a single port (e.g. 8080) for all of your endpoints.

If you wish /healthz to allow HTTP traffic, then you will need to create a second Ingress resource without TLS:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
...
spec:
  rules:
  - http:
      paths:
      - path: /healthz
        pathType: Prefix
        backend:
          serviceName: [service]
          servicePort: 8080

More Info

Regarding your question about "linking" the nginx Ingress Controller with your Node.js app - the Ingress Controller would point to the ClusterIP Service specified in the Ingress resources' backend fields.

Remember to deploy your Ingress resources in the same Namespaces as the Services they point to for simplicity. Otherwise, you would have to make a Service of type ExternalName in the Ingress resource's Namespace which points to the Service in the other Namespace. See here.

Your Ingress Controller can be in any Namespace, assuming it is configured to watch all Namespaces for Ingress resources.

Serge
  • 634
  • 4
  • 9
  • Thanks for the answer, as it made things more clear to me! I've modified the code and it indeed works, but now I've got another problem:When I access the app with `curl` on `https`, I correctly get `200`, but when I try to access the endpoint from another `node` app, I then get `request to https://localhost:8080 failed, reason: unable to verify the first certificate`. _Also, the two containers I've mentioned, was just a tought about somehow using `node.js` and `nginx` together._ – Esskimo Jul 30 '20 at 11:57
  • If the other Node app is using `localhost` then it would have to be in the same Pod as the container its trying to hit. Otherwise, if its in a separate Pod, it could use the A record of the ClusterIP Service of the container its trying to hit. As a side not, if you have an off-cluster Node app that needs to hit the Ingress Service, you would need to make sure that app has the root cert, check this answer out: https://stackoverflow.com/questions/31673587/error-unable-to-verify-the-first-certificate-in-nodejs – Serge Jul 30 '20 at 19:30
  • I have updated my answer to address the nginx-node.js link clarification you made in above comment. TY – Serge Jul 30 '20 at 19:32