Redirecting http to https in express not working in EKS

1/24/2020

I have a kubernetes cluster and an express server serving a SPA. Currently if I hit the http version of my website I do not redirect to https, but I would like to.

This is what I've tried -

import express from "express";
const PORT = 3000;
const path = require("path");
const app = express();
const router = express.Router();

const forceHttps = function(req, res, next) {
  const xfp =
    req.headers["X-Forwarded-Proto"] || req.headers["x-forwarded-proto"];
  if (xfp === "http") {
    console.log("host name");
    console.log(req.hostname);
    console.log(req.url);
    const redirectTo = `https:\/\/${req.hostname}${req.url}`;
    res.redirect(301, redirectTo);
  } else {
    next();
  }
};

app.get("/*", forceHttps);

// root (/) should always serve our server rendered page
// other static resources should be served as they are
const root = path.resolve(__dirname, "..", "build");
app.use(express.static(root, { maxAge: "30d" }));
app.get("/*", function(req, res, next) {
  if (
    req.method === "GET" &&
    req.accepts("html") &&
    !req.is("json") &&
    !req.path.includes(".")
  ) {
    res.sendFile("index.html", { root });
  } else {
    next();
  }
});

// tell the app to use the above rules
app.use(router);

app.listen(PORT, error => {
  console.log(`listening on ${PORT} from the server`);
  if (error) {
    console.log(error);
  }
});

This is what my kubernetes config looks like

apiVersion: v1
kind: Service
metadata:
  name: <NAME>
  labels:
    app: <APP>
  annotations:
    # Note that the backend talks over HTTP.
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: <CERT>
    # Only run SSL on the port named "https" below.
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
spec:
  type: LoadBalancer
  selector:
    app: <APP>
  ports:
    - port: 443
      targetPort: 3000
      protocol: TCP
      name: https
    - port: 80
      targetPort: 3000
      protocol: TCP
      name: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: <DEPLOYMENT_NAME>
  labels:
    app: <APP>
spec:
  replicas: 1
  selector:
    matchLabels:
      app: <APP>
  template:
    metadata:
      labels:
        app: <APP>
    spec:
      containers:
        - name: <CONTAINER_NAME>
          image: DOCKER_IMAGE_NAME
          imagePullPolicy: Always
          env:
            - name: VERSION_INFO
              value: "1.0"
            - name: BUILD_DATE
              value: "1.0"
          ports:
            - containerPort: 3000

I successfully hit the redirect...but the browser does not actually redirect. How do I get it to redirect from http to https?

Relatedly, from googling around I keep seeing that people are using an Ingress AND a Load Balancer - why would I need both?

-- praks5432
express
kubernetes
node.js

1 Answer

1/24/2020

When you LoadBalancer type service in EKS it will either create a classic or a network load balancer.None of these support http to https redirection. You need an application load balancer(ALB) which supports http to https redirection.In EKS to use ALB you need to use AWS ALB ingress controller. Once you have ingress controller setup you can use annotation to redirect http to https

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: default
  name: ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-west-2:xxxx:certificate/xxxxxx
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}
-- Arghya Sadhu
Source: StackOverflow