Test web page on local K8s via HTTPS w/o specifying port

When I deploy some web application on the local Kubernetes cluster, I would like to test it like

  • Without specifying a port number
  • Via HTTPS
  • Specify original FQDN not localhost

For example, if I'm developing "www.example.com," I would like to test by the URL like https://www.example.work/. So, Let me share how to do it.

When deploying a web site on the Kubernetes on the local computer, I need to test the site with the URL like https://localhost:30000. But this is bothersome for testing because it depends on the web site, some links in the page don’t have the port number. And also, nowadays, most of the web site should be accessed via HTTPS, so I want to test the site via HTTPS. So I made a load balancer to solve the problem with NGINX.

At first, I put the settings for redirecting from HTTP to HTTPS with the 307 status like this.

 1server {
 2    listen       80;
 3    server_name  _;
 4
 5    location = /any.example.com.pem {
 6        root   /usr/share/nginx/html;
 7    }
 8
 9    location / {
10        return 307 https://$host$request_uri;
11    }
12}

The above setting includes a setting to download the dummy SSL cert file which I will explain later. And I set the redirecting status to 307, not 301 to keep the request's method and body just in case.

And then, I put the settings for HTTPS like this.

 1map $http_host $backend {
 2    default $http_host;
 3    example.work     web_example;
 4    example.com      web_example;
 5}
 6
 7upstream web_example {
 8    server host.docker.internal:30000;
 9}
10
11server {
12    listen       443 default ssl;
13    server_name  _;
14
15    # SSL
16    ssl_certificate      /usr/local/nginx/cert/any.example.com.pem;
17    ssl_certificate_key  /usr/local/nginx/cert/any.example.com.key;  
18
19    location / {
20        proxy_set_header Host                   $host;
21        proxy_set_header X-Real-IP              $remote_addr;
22        proxy_set_header X-Forwarded-Proto      https;
23        proxy_set_header X-Forwarded-Host       $host;
24        proxy_set_header X-Forwarded-Server     $host;
25        proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
26
27        proxy_pass https://$backend;
28
29        send_timeout          180;
30        proxy_connect_timeout 600;
31        proxy_read_timeout    600;
32        proxy_send_timeout    600;
33    }
34}

The above setting accepts the following FQDNs.

1www.example.com
2www.example.work

because when developing on the local environment, some slightly different FQDN, www.example.work in this case, is very useful for avoiding to modify the "hosts file" time and again. After accepting the request, NGINX transfers it to 30000 port on the localhost with some additional headers. The port should be changed depending on your target.

Before running the above NGINX, I need to create a self-cert file. I'll create it with a simple docker image like this.

1FROM alpine:latest
2
3RUN apk update \
4    && apk add --no-cache \
5                openssl

And then, create an OpenSSL setting file like this.

 1[req]
 2default_bits       = 2048
 3distinguished_name = req_distinguished_name
 4req_extensions     = v3_req
 5x509_extensions    = v3_ca
 6prompt             = no
 7
 8[ v3_ca ]
 9subjectKeyIdentifier    = hash
10authorityKeyIdentifier  = keyid:always,issuer
11basicConstraints        = CA:FALSE
12
13[ req_distinguished_name ]
14countryName             = JP
15stateOrProvinceName     = Tokyo
16localityName            = Shibuya
17organizationName        = Example Dot Com
18organizationalUnitName	= Infrastructure 
19commonName              = example.com
20
21[ v3_req ]
22# Extensions to add to a certificate request
23basicConstraints    = CA:FALSE
24keyUsage            = nonRepudiation, digitalSignature, keyEncipherment
25extendedKeyUsage    = clientAuth, serverAuth
26subjectAltName      = @alt_names
27
28[ alt_names ]
29DNS.1 = localhost
30DNS.2 = example.com
31DNS.3 = *.example.com
32DNS.4 = example.work
33DNS.5 = *.example.work

And then, create docker-entrypoint.sh to generate cert files with the above setting file.

 1#!/bin/ash
 2dir="/cert"
 3prefix="any.example.com"
 4file_path_prefix="${dir}/${prefix}"
 5
 6# Output files
 7intermediate_file="${dir}/dummy.cer"
 8cert_file="${file_path_prefix}.crt"
 9csr_file="${file_path_prefix}.csr"
10key_file="${file_path_prefix}.key"
11output_pem="${file_path_prefix}.pem"
12
13if [ ! -f $output_pem ];then
14    # Generate key and certificate files
15    openssl req -sha256 -new -newkey rsa:4096 -days 3650 -nodes -x509 \
16        -extensions v3_req \
17        -config /openssl.cnf \
18        -keyout $key_file \
19        -out    $cert_file
20
21    # Generate dummy files
22    touch $csr_file
23    touch $intermediate_file
24
25    # Gen integrated PEM file
26    cat \
27    $cert_file \
28    $intermediate_file \
29    $key_file \
30    > $output_pem
31else
32    echo "Already existed."
33fi
34
35# Keep running
36tail -f /dev/null

And then, create a docker-compose.yml to run the above 2 applications. Notice that it will use 80 and 443 ports so it’s necessary to quit in advance if some other application is using.

 1version: '3.8'
 2services:
 3  test-lb-nginx:
 4    image: nginx:alpine
 5    restart: always
 6    container_name: dev-elb
 7    ports:
 8      - 80:80
 9      - 443:443
10    volumes:
11      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
12      - ./docker/nginx/conf.d:/etc/nginx/conf.d:rw
13      - ./docker/nginx/cert:/usr/local/nginx/cert:ro
14      - ./docker/nginx/cert:/usr/share/nginx/html:ro
15    depends_on: 
16      - gen-self-cert
17
18  gen-self-cert:
19    build:
20      context: ./docker/self_cert
21    restart: always
22    container_name: gen-cert
23    volumes:
24      - ./docker/self_cert/docker-entrypoint.sh:/docker-entrypoint.sh:ro
25      - ./docker/self_cert/openssl.cnf:/openssl.cnf:ro
26      - ./docker/nginx/cert:/cert
27    entrypoint: "/docker-entrypoint.sh"

And run with the following command.

1$ docker-compose up -d

Access https://127.0.0.1/any.example.com.pem to get the generated cert file and then it should be imported to the system. (The way to import is different it depends on your OS)

Before accessing the above containers, it's necessary to add some FQDNs which I want to test like this.

1127.0.0.1 www.example.work

Finally, it's possible to get access by the following URL without specifying port via HTTPS. https://www.example.work/