This commit is contained in:
Yves Peter 2017-08-19 18:20:25 +00:00 committed by GitHub
commit fbce22d626
4 changed files with 149 additions and 66 deletions

View file

@ -50,6 +50,8 @@ Usage of :
--configmap string Name of the ConfigMap that contains the custom configuration use
--default-backend-service string Service used to serve a 404 page for the default backend. Takes the form namespace/name. The controller uses the first node port of this Service for the default backend.
--default-ssl-certificate string Name of the secret that contains a SSL certificate to be used as default for a HTTPS catch-all server
--always-enable-tls bool Enable tls using the default-ssl-certificate even if no tls section is defined
for the ingress.
--election-id string Election id to use for status update. (default "ingress-controller-leader")
--force-namespace-isolation Force namespace isolation. This flag is required to avoid the reference of secrets or configmaps located in a different namespace than the specified in the flag --watch-namespace.
--health-check-path string Defines the URL to be used as health check inside in the default server in NGINX. (default "/healthz")
@ -68,7 +70,7 @@ Usage of :
The ports 80 and 443 are not allowed as external ports. This ports are reserved for the backend
--udp-services-configmap string Name of the ConfigMap that contains the definition of the UDP services to expose.
The key in the map indicates the external port to be used. The value is the name of the service with the format namespace/serviceName and the port of the service could be a number of the name of the port.
--update-status Indicates if the ingress controller should update the Ingress status IP/hostname. Default is true (default true)
--update-status bool Indicates if the ingress controller should update the Ingress status IP/hostname. Default is true (default true)
-v, --v Level log level for V logs
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
--watch-namespace string Namespace to watch for Ingress. Default is to watch all namespaces

View file

@ -130,6 +130,7 @@ type Configuration struct {
// optional
UDPConfigMapName string
DefaultSSLCertificate string
AlwaysEnableTLS bool
DefaultHealthzURL string
DefaultIngressClass string
// optional
@ -340,7 +341,7 @@ func (ic GenericController) GetDefaultBackend() defaults.Backend {
}
// GetRecorder returns the event recorder
func (ic GenericController) GetRecoder() record.EventRecorder {
func (ic GenericController) GetRecorder() record.EventRecorder {
return ic.recorder
}
@ -1064,76 +1065,80 @@ func (ic *GenericController) createServers(data []interface{},
},
}, SSLPassthrough: sslpt}
}
}
// configure default location and SSL
for _, ingIf := range data {
ing := ingIf.(*extensions.Ingress)
if !class.IsValid(ing, ic.cfg.IngressClass, ic.cfg.DefaultIngressClass) {
continue
}
for _, rule := range ing.Spec.Rules {
host := rule.Host
if host == "" {
host = defServerName
}
// only add a certificate if the server does not have one previously configured
if len(ing.Spec.TLS) == 0 || servers[host].SSLCertificate != "" {
continue
}
tlsSecretName := ""
found := false
for _, tls := range ing.Spec.TLS {
if sets.NewString(tls.Hosts...).Has(host) {
tlsSecretName = tls.SecretName
found = true
break
}
}
// the current ing.Spec.Rules[].Host doesn't have an entry at
// ing.Spec.TLS[].Hosts[] skipping to the next Rule
if !found {
continue
}
if tlsSecretName == "" {
glog.V(3).Infof("host %v is listed on tls section but secretName is empty. Using default cert", host)
servers[host].SSLCertificate = defaultPemFileName
servers[host].SSLPemChecksum = defaultPemSHA
continue
}
key := fmt.Sprintf("%v/%v", ing.Namespace, tlsSecretName)
bc, exists := ic.sslCertTracker.Get(key)
if !exists {
glog.Infof("ssl certificate \"%v\" does not exist in local store", key)
continue
}
cert := bc.(*ingress.SSLCert)
err = cert.Certificate.VerifyHostname(host)
if err != nil {
glog.Warningf("ssl certificate %v does not contain a Common Name or Subject Alternative Name for host %v", key, host)
continue
}
servers[host].SSLCertificate = cert.PemFileName
servers[host].SSLPemChecksum = cert.PemSHA
servers[host].SSLExpireTime = cert.ExpireTime
if cert.ExpireTime.Before(time.Now().Add(240 * time.Hour)) {
glog.Warningf("ssl certificate for host %v is about to expire in 10 days", host)
}
}
ic.configureTLSforIng(ing, servers, defaultPemFileName, defaultPemSHA)
}
return servers
}
func (ic *GenericController) configureTLSforIng(ing *extensions.Ingress, servers map[string]*ingress.Server, defaultPemFileName string, defaultPemSHA string) {
for _, rule := range ing.Spec.Rules {
host := rule.Host
if host == "" {
host = defServerName
}
// only add a certificate if the server does not have one previously configured
if servers[host].SSLCertificate != "" {
continue
}
tlsForHost := getTLSForHost(ing.Spec.TLS, host)
if tlsForHost == nil && ic.cfg.AlwaysEnableTLS {
glog.V(3).Infof("no tls host defined for %v but EnforceTLS is true. Using default cert", host)
servers[host].SSLCertificate = defaultPemFileName
servers[host].SSLPemChecksum = defaultPemSHA
continue
}
// the current ing.Spec.Rules[].Host doesn't have an entry at
// ing.Spec.TLS[].Hosts[] skipping to the next Rule
if tlsForHost == nil {
continue
}
if tlsForHost.SecretName == "" {
glog.V(3).Infof("host %v is listed on tls section but secretName is empty. Using default cert", host)
servers[host].SSLCertificate = defaultPemFileName
servers[host].SSLPemChecksum = defaultPemSHA
continue
}
key := fmt.Sprintf("%v/%v", ing.Namespace, tlsForHost.SecretName)
bc, exists := ic.sslCertTracker.Get(key)
if !exists {
glog.Infof("ssl certificate \"%v\" does not exist in local store", key)
continue
}
cert := bc.(*ingress.SSLCert)
err := cert.Certificate.VerifyHostname(host)
if err != nil {
glog.Warningf("ssl certificate %v does not contain a Common Name or Subject Alternative Name for host %v", key, host)
continue
}
servers[host].SSLCertificate = cert.PemFileName
servers[host].SSLPemChecksum = cert.PemSHA
servers[host].SSLExpireTime = cert.ExpireTime
if cert.ExpireTime.Before(time.Now().Add(240 * time.Hour)) {
glog.Warningf("ssl certificate for host %v is about to expire in 10 days", host)
}
}
}
func getTLSForHost(ingressTLS []extensions.IngressTLS, host string) *extensions.IngressTLS {
for _, tls := range ingressTLS {
if sets.NewString(tls.Hosts...).Has(host) {
return &tls
}
}
return nil
}
// getEndpoints returns a list of <endpoint ip>:<port> for a given service/target port combination.
func (ic *GenericController) getEndpoints(
s *api.Service,

View file

@ -0,0 +1,72 @@
package controller
import (
"testing"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress/core/pkg/ingress"
)
func buildGenericControllerForCertTest() *GenericController {
return &GenericController{
cfg: &Configuration{},
}
}
func buildTestIngressRuleForCertTest() extensions.Ingress {
return extensions.Ingress{
Spec: extensions.IngressSpec{
Rules: []extensions.IngressRule{
extensions.IngressRule{
Host: "hostWithTLS",
},
extensions.IngressRule{
Host: "hostWithoutTLS",
},
},
TLS: []extensions.IngressTLS{
extensions.IngressTLS{
Hosts: []string{"hostWithTLS"},
},
},
},
}
}
func buildTestServersForCertTest() map[string]*ingress.Server {
return map[string]*ingress.Server{
"hostWithTLS": &ingress.Server{
Hostname: "hostWithTLS",
},
"hostWithoutTLS": &ingress.Server{
Hostname: "hostWithoutTLS",
},
}
}
func TestConfigureTLSforIng(t *testing.T) {
ic := buildGenericControllerForCertTest()
testIng := buildTestIngressRuleForCertTest()
testServers := buildTestServersForCertTest()
defaultPemFileName := "defaultPemFileName"
defaultPemSHA := "defaultPemSHA"
ic.configureTLSforIng(&testIng, testServers, defaultPemFileName, defaultPemSHA)
if testServers["hostWithTLS"].SSLCertificate != defaultPemFileName {
t.Errorf("SSLCertificate set to %s instead of %s", testServers["hostWithTLS"].SSLCertificate, defaultPemFileName)
}
if testServers["hostWithoutTLS"].SSLCertificate != "" {
t.Errorf("SSLCertificate set to %s instead of being empty", testServers["hostWithoutTLS"].SSLCertificate)
}
ic.cfg.AlwaysEnableTLS = true
ic.configureTLSforIng(&testIng, testServers, defaultPemFileName, defaultPemSHA)
if testServers["hostWithTLS"].SSLCertificate != defaultPemFileName {
t.Errorf("SSLCertificate set to %s instead of %s", testServers["hostWithTLS"].SSLCertificate, defaultPemFileName)
}
if testServers["hostWithoutTLS"].SSLCertificate != defaultPemFileName {
t.Errorf("SSLCertificate set to %s instead of %s", testServers["hostWithoutTLS"].SSLCertificate, defaultPemFileName)
}
}

View file

@ -78,6 +78,9 @@ func NewIngressController(backend ingress.Controller) *GenericController {
defSSLCertificate = flags.String("default-ssl-certificate", "", `Name of the secret
that contains a SSL certificate to be used as default for a HTTPS catch-all server`)
defAlwaysEnableTLS = flags.Bool("always-enable-tls", false, `Enable tls using the default-ssl-certificate even if no tls section is defined
for the ingress.`)
defHealthzURL = flags.String("health-check-path", "/healthz", `Defines
the URL to be used as health check inside in the default server in NGINX.`)
@ -171,6 +174,7 @@ func NewIngressController(backend ingress.Controller) *GenericController {
TCPConfigMapName: *tcpConfigMapName,
UDPConfigMapName: *udpConfigMapName,
DefaultSSLCertificate: *defSSLCertificate,
AlwaysEnableTLS: *defAlwaysEnableTLS,
DefaultHealthzURL: *defHealthzURL,
PublishService: *publishSvc,
Backend: backend,