Ingress-nginx: How to https redirect naked domain to www using from-to-www-redirect

Created on 7 Jun 2020  路  2Comments  路  Source: kubernetes/ingress-nginx

Hello,

I am not sure if it is a bug, but still I can't https redirect naked domain to www subdomain using from-to-www-redirect annotation. I did lot of tests checking the ingress-controller logs and resulted Nginx config, and I will show you what is going on. AFAIK I need one server block for naked domain and one for www subdomain in order to handle SSL handshake.


  1. I create a block for example.com
    Block example.com created without redirection.
  2. I try to create a block for www.example.com with from-to-www-redirect
    Logs says: Already exists an Ingress with "example.com" hostname. Skipping creation of redirection from "example.com" to "www.example.com".

    Block www.example.com created without redirection.


// Not what I want because would redirect to naked but attempted anyway

  1. I create a block for www.example.com
    Block www.example.com created without redirection.
  2. I try to create a block for example.com with from-to-www-redirect
    Logs says: Already exists an Ingress with "www.example.com" hostname. Skipping creation of redirection from "www.example.com" to "example.com".

Block example.com created without redirection.


  1. I create a block for www.example.com with from-to-www-redirect
    Logs: the server example.com has SSL configured but the SSL certificate does not contains a CN for www.example.com. Redirects will not work for HTTPS to HTTPS
    Redirect block "server_name example.com" added to nginx.conf
  2. I create a block for example.com without from-to-www-redirect
    Logs: Already exists an Ingress with "example.com" hostname. Skipping creation of redirection from "example.com" to "www.example.com"

    Redirect block is removed from nginx conf!


// Not what I want because would redirect to naked but attempted anyway

  1. I create a block for example.com with from-to-www-redirect
    Logs: the server www.example.com has SSL configured but the SSL certificate does not contains a CN for example.com. Redirects will not work for HTTPS to HTTPS
    Redirect block is added to nginx.conf
  2. I create a block for www.example.com without from-to-www-redirect
    Logs: Already exists an Ingress with "www.example.com" hostname. Skipping creation of redirection from "www.example.com" to "example.com".

Redirect block is removed from nginx conf!


  1. I create a block for www.example.com with from-to-www-redirect
    Logs the server example.com has SSL configured but the SSL certificate does not contains a CN for www.example.com. Redirects will not work for HTTPS to HTTPS
    Redirect block "server_name example.com" added to nginx.conf
  2. I create a block for example.com with from-to-www-redirect
    Logs:
  3. Already exists an Ingress with "www.example.com" hostname. Skipping creation of redirection from "www.example.com" to "example.com".
  4. Already exists an Ingress with "example.com" hostname. Skipping creation of redirection from "example.com" to "www.example.com".

    Redirect blocks are removed from nginx conf!

So the next question is, what am I doing wrong and how people normally do HTTPS to HTTPS redirection?! I can't be the first person to came across this.

kinsupport

Most helpful comment

You can test this with the next script

#!/bin/bash

# kind
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
EOF

# ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yaml

# dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/docs/examples/http-svc.yaml

# change the IP address
export MY_IP=192.168.1.193

# create a SSL cert
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout foo.key -out foo.crt \
  -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=$MY_IP.xip.io" \
  -addext "subjectAltName=DNS:www.foo.$MY_IP.xip.io,DNS:foo.$MY_IP.xip.io,IP:$MY_IP"

kubectl create secret tls foo-xip-tls --cert=foo.crt --key=foo.key

echo "
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: http-svc
  annotations:
    nginx.ingress.kubernetes.io/from-to-www-redirect: 'true'
spec:
  tls:
    - hosts:
      - $MY_IP.xip.io
      - www.$MY_IP.xip.io
      secretName: foo-xip-tls
  rules:
  - host: foo.$MY_IP.xip.io
    http:
      paths:
      - path: /
        backend:
          serviceName: http-svc
          servicePort: 80
" | kubectl apply -f -
curl https://www.foo.192.168.1.193.xip.io -v -k
*   Trying 192.168.1.193:443...
* TCP_NODELAY set
* Connected to www.foo.192.168.1.193.xip.io (192.168.1.193) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; O=MyOrg, Inc.; CN=192.168.1.193.xip.io
*  start date: Jun  7 14:29:10 2020 GMT
*  expire date: Jun  5 14:29:10 2030 GMT
*  issuer: C=US; ST=CA; O=MyOrg, Inc.; CN=192.168.1.193.xip.io
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x56171b475db0)
> GET / HTTP/2
> Host: www.foo.192.168.1.193.xip.io
> user-agent: curl/7.68.0
> accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 308 
< server: nginx/1.17.10
< date: Sun, 07 Jun 2020 14:33:39 GMT
< content-type: text/html
< content-length: 172
< location: https://foo.192.168.1.193.xip.io/
< 
<html>
<head><title>308 Permanent Redirect</title></head>
<body>
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx/1.17.10</center>
</body>
</html>
* Connection #0 to host www.foo.192.168.1.193.xip.io left intact

All 2 comments

To be able to redirect HTTPS to HTTPS the secret defined in the ingress must contain a CN for both hosts, otherwise, you will see something like this in the log:
W0607 14:28:14.027827 8 nginx.go:1208] the server www.foo.192.168.1.193.xip.io has SSL configured but the SSL certificate does not contains a CN for foo.192.168.1.193.xip.io. Redirects will not work for HTTPS to HTTPS

You can test this with the next script

#!/bin/bash

# kind
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
EOF

# ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yaml

# dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/docs/examples/http-svc.yaml

# change the IP address
export MY_IP=192.168.1.193

# create a SSL cert
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout foo.key -out foo.crt \
  -subj "/C=US/ST=CA/O=MyOrg, Inc./CN=$MY_IP.xip.io" \
  -addext "subjectAltName=DNS:www.foo.$MY_IP.xip.io,DNS:foo.$MY_IP.xip.io,IP:$MY_IP"

kubectl create secret tls foo-xip-tls --cert=foo.crt --key=foo.key

echo "
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: http-svc
  annotations:
    nginx.ingress.kubernetes.io/from-to-www-redirect: 'true'
spec:
  tls:
    - hosts:
      - $MY_IP.xip.io
      - www.$MY_IP.xip.io
      secretName: foo-xip-tls
  rules:
  - host: foo.$MY_IP.xip.io
    http:
      paths:
      - path: /
        backend:
          serviceName: http-svc
          servicePort: 80
" | kubectl apply -f -
curl https://www.foo.192.168.1.193.xip.io -v -k
*   Trying 192.168.1.193:443...
* TCP_NODELAY set
* Connected to www.foo.192.168.1.193.xip.io (192.168.1.193) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; O=MyOrg, Inc.; CN=192.168.1.193.xip.io
*  start date: Jun  7 14:29:10 2020 GMT
*  expire date: Jun  5 14:29:10 2030 GMT
*  issuer: C=US; ST=CA; O=MyOrg, Inc.; CN=192.168.1.193.xip.io
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x56171b475db0)
> GET / HTTP/2
> Host: www.foo.192.168.1.193.xip.io
> user-agent: curl/7.68.0
> accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 308 
< server: nginx/1.17.10
< date: Sun, 07 Jun 2020 14:33:39 GMT
< content-type: text/html
< content-length: 172
< location: https://foo.192.168.1.193.xip.io/
< 
<html>
<head><title>308 Permanent Redirect</title></head>
<body>
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx/1.17.10</center>
</body>
</html>
* Connection #0 to host www.foo.192.168.1.193.xip.io left intact
Was this page helpful?
0 / 5 - 0 ratings