Add support for redirect https to https when from-to-www-redirect is defined

This commit is contained in:
Manuel Alejandro de Brito Fontes 2019-01-09 00:33:16 -03:00
parent 35f5a6ce1f
commit a3bcbeb3d2
No known key found for this signature in database
GPG key ID: 786136016A8BA02A
6 changed files with 196 additions and 31 deletions

View file

@ -721,7 +721,7 @@ type TemplateConfig struct {
IsSSLPassthroughEnabled bool
NginxStatusIpv4Whitelist []string
NginxStatusIpv6Whitelist []string
RedirectServers map[string]string
RedirectServers interface{}
ListenPorts *ListenPorts
PublishService *apiv1.Service
DynamicCertificatesEnabled bool

View file

@ -38,6 +38,7 @@ import (
"github.com/eapache/channels"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/kubernetes/scheme"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/record"
@ -498,39 +499,20 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
// https://trac.nginx.org/nginx/ticket/631
var longestName int
var serverNameBytes int
redirectServers := make(map[string]string)
for _, srv := range ingressCfg.Servers {
if longestName < len(srv.Hostname) {
longestName = len(srv.Hostname)
}
serverNameBytes += len(srv.Hostname)
if srv.RedirectFromToWWW {
var n string
if strings.HasPrefix(srv.Hostname, "www.") {
n = strings.TrimPrefix(srv.Hostname, "www.")
} else {
n = fmt.Sprintf("www.%v", srv.Hostname)
}
klog.V(3).Infof("Creating redirect from %q to %q", srv.Hostname, n)
if _, ok := redirectServers[n]; !ok {
found := false
for _, esrv := range ingressCfg.Servers {
if esrv.Hostname == n {
found = true
break
}
}
if !found {
redirectServers[n] = srv.Hostname
}
}
}
}
if cfg.ServerNameHashBucketSize == 0 {
nameHashBucketSize := nginxHashBucketSize(longestName)
klog.V(3).Infof("Adjusting ServerNameHashBucketSize variable to %d", nameHashBucketSize)
cfg.ServerNameHashBucketSize = nameHashBucketSize
}
serverNameHashMaxSize := nextPowerOf2(serverNameBytes)
if cfg.ServerNameHashMaxSize < serverNameHashMaxSize {
klog.V(3).Infof("Adjusting ServerNameHashMaxSize variable to %d", serverNameHashMaxSize)
@ -619,7 +601,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6,
NginxStatusIpv4Whitelist: cfg.NginxStatusIpv4Whitelist,
NginxStatusIpv6Whitelist: cfg.NginxStatusIpv6Whitelist,
RedirectServers: redirectServers,
RedirectServers: buildRedirects(ingressCfg.Servers),
IsSSLPassthroughEnabled: n.cfg.EnableSSLPassthrough,
ListenPorts: n.cfg.ListenPorts,
PublishService: n.GetPublishService(),
@ -1022,3 +1004,65 @@ func cleanTempNginxCfg() error {
return nil
}
type redirect struct {
From string
To string
SSLCert ingress.SSLCert
}
func buildRedirects(servers []*ingress.Server) []*redirect {
names := sets.String{}
redirectServers := make([]*redirect, 0)
for _, srv := range servers {
if !srv.RedirectFromToWWW {
continue
}
to := srv.Hostname
var from string
if strings.HasPrefix(to, "www.") {
from = strings.TrimPrefix(to, "www.")
} else {
from = fmt.Sprintf("www.%v", to)
}
if names.Has(to) {
continue
}
klog.V(3).Infof("Creating redirect from %q to %q", from, to)
found := false
for _, esrv := range servers {
if esrv.Hostname == from {
found = true
break
}
}
if found {
klog.Warningf("Already exists an Ingress with %q hostname. Skipping creation of redirection from %q to %q.", from, from, to)
continue
}
r := &redirect{
From: from,
To: to,
}
if srv.SSLCert.PemSHA != "" {
if ssl.IsValidHostname(from, srv.SSLCert.CN) {
r.SSLCert = srv.SSLCert
} else {
klog.Warningf("the server %v has SSL configured but the SSL certificate does not contains a CN for %v. Redirects will not work for HTTPS to HTTPS", from, to)
}
}
redirectServers = append(redirectServers, r)
names.Insert(to)
}
return redirectServers
}

View file

@ -30,6 +30,7 @@ import (
"math/big"
"net"
"strconv"
"strings"
"time"
"github.com/zakjan/cert-chain-resolver/certUtil"
@ -508,3 +509,21 @@ func FullChainCert(in string, fs file.Filesystem) ([]byte, error) {
return certUtil.EncodeCertificates(certs), nil
}
// IsValidHostname checks if a hostname is valid in a list of common names
func IsValidHostname(hostname string, commonNames []string) bool {
for _, cn := range commonNames {
if strings.EqualFold(hostname, cn) {
return true
}
labels := strings.Split(hostname, ".")
labels[0] = "*"
candidate := strings.Join(labels, ".")
if strings.EqualFold(candidate, cn) {
return true
}
}
return false
}

View file

@ -205,3 +205,39 @@ func newCA(name string) (*keyPair, error) {
Cert: cert,
}, nil
}
func TestIsValidHostname(t *testing.T) {
cases := map[string]struct {
Hostname string
CN []string
Valid bool
}{
"when there is no common names": {
"foo.bar",
[]string{},
false,
},
"when there is a match for foo.bar": {
"foo.bar",
[]string{"foo.bar"},
true,
},
"when there is a wildcard match for foo.bar": {
"foo.bar",
[]string{"*.bar"},
true,
},
"when there is a wrong wildcard for *.bar": {
"invalid.foo.bar",
[]string{"*.bar"},
false,
},
}
for k, tc := range cases {
valid := IsValidHostname(tc.Hostname, tc.CN)
if valid != tc.Valid {
t.Errorf("%s: expected '%v' but returned %v", k, tc.Valid, valid)
}
}
}