Merge branch 'master' of https://github.com/kubernetes/ingress-nginx into proxyssl

This commit is contained in:
Gabor Lekeny 2019-08-16 06:21:53 +02:00
commit 65b9e2c574
391 changed files with 23957 additions and 20447 deletions

View file

@ -18,6 +18,7 @@ package controller
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
@ -63,7 +64,7 @@ func (n *NGINXController) Check(_ *http.Request) error {
if err != nil {
return errors.Wrap(err, "unexpected error reading /proc directory")
}
f, err := n.fileSystem.ReadFile(nginx.PID)
f, err := ioutil.ReadFile(nginx.PID)
if err != nil {
return errors.Wrapf(err, "unexpected error reading %v", nginx.PID)
}

View file

@ -26,7 +26,6 @@ import (
"testing"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/kubernetes/pkg/util/filesystem"
"k8s.io/ingress-nginx/internal/file"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
@ -55,14 +54,10 @@ func TestNginxCheck(t *testing.T) {
defer server.Close()
server.Start()
// mock filesystem
fs := filesystem.DefaultFs{}
n := &NGINXController{
cfg: &Configuration{
ListenPorts: &ngx_config.ListenPorts{},
},
fileSystem: fs,
}
t.Run("no pid or process", func(t *testing.T) {
@ -72,8 +67,8 @@ func TestNginxCheck(t *testing.T) {
})
// create pid file
fs.MkdirAll("/tmp", file.ReadWriteByUser)
pidFile, err := fs.Create(nginx.PID)
os.MkdirAll("/tmp", file.ReadWriteByUser)
pidFile, err := os.Create(nginx.PID)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View file

@ -33,8 +33,6 @@ import (
var (
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
EnableSSLChainCompletion = false
// EnableDynamicCertificates Dynamically update SSL certificates instead of reloading NGINX
EnableDynamicCertificates = true
)
const (
@ -75,6 +73,10 @@ const (
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols
sslProtocols = "TLSv1.2"
// Disable TLS 1.3 early data
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
sslEarlyData = false
// Time during which a client may reuse the session parameters stored in a cache.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_timeout
sslSessionTimeout = "10m"
@ -124,11 +126,6 @@ type Configuration struct {
// By default error logs go to /var/log/nginx/error.log
ErrorLogPath string `json:"error-log-path,omitempty"`
// EnableDynamicTLSRecords enables dynamic TLS record sizes
// https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency
// By default this is enabled
EnableDynamicTLSRecords bool `json:"enable-dynamic-tls-records"`
// EnableModsecurity enables the modsecurity module for NGINX
// By default this is disabled
EnableModsecurity bool `json:"enable-modsecurity"`
@ -317,6 +314,10 @@ type Configuration struct {
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols
SSLProtocols string `json:"ssl-protocols,omitempty"`
// Enables or disable TLS 1.3 early data.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
SSLEarlyData bool `json:"ssl-early-data,omitempty"`
// Enables or disables the use of shared SSL cache among worker processes.
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache
SSLSessionCache bool `json:"ssl-session-cache,omitempty"`
@ -607,6 +608,13 @@ type Configuration struct {
// Block all requests with given Referer headers
BlockReferers []string `json:"block-referers"`
// Lua shared dict configuration data / certificate data
LuaSharedDicts map[string]int `json:"lua-shared-dicts"`
// DefaultSSLCertificate holds the default SSL certificate to use in the configuration
// It can be the fake certificate or the one behind the flag --default-ssl-certificate
DefaultSSLCertificate *ingress.SSLCert `json:"-"`
}
// NewDefault returns the default nginx configuration
@ -640,7 +648,6 @@ func NewDefault() Configuration {
ClientHeaderTimeout: 60,
ClientBodyBufferSize: "8k",
ClientBodyTimeout: 60,
EnableDynamicTLSRecords: true,
EnableUnderscoresInHeaders: false,
ErrorLogLevel: errorLevel,
UseForwardedHeaders: false,
@ -683,6 +690,7 @@ func NewDefault() Configuration {
SSLCiphers: sslCiphers,
SSLECDHCurve: "auto",
SSLProtocols: sslProtocols,
SSLEarlyData: sslEarlyData,
SSLSessionCache: true,
SSLSessionCacheSize: sslSessionCacheSize,
SSLSessionTickets: true,
@ -720,6 +728,7 @@ func NewDefault() Configuration {
LimitRateAfter: 0,
ProxyBuffering: "off",
ProxyHTTPVersion: "1.1",
ProxyMaxTempFileSize: "1024m",
},
UpstreamKeepaliveConnections: 32,
UpstreamKeepaliveTimeout: 60,
@ -767,25 +776,24 @@ func (cfg Configuration) BuildLogFormatUpstream() string {
// TemplateConfig contains the nginx configuration to render the file nginx.conf
type TemplateConfig struct {
ProxySetHeaders map[string]string
AddHeaders map[string]string
BacklogSize int
Backends []*ingress.Backend
PassthroughBackends []*ingress.SSLPassthroughBackend
Servers []*ingress.Server
TCPBackends []ingress.L4Service
UDPBackends []ingress.L4Service
HealthzURI string
Cfg Configuration
IsIPV6Enabled bool
IsSSLPassthroughEnabled bool
NginxStatusIpv4Whitelist []string
NginxStatusIpv6Whitelist []string
RedirectServers interface{}
ListenPorts *ListenPorts
PublishService *apiv1.Service
EnableDynamicCertificates bool
EnableMetrics bool
ProxySetHeaders map[string]string
AddHeaders map[string]string
BacklogSize int
Backends []*ingress.Backend
PassthroughBackends []*ingress.SSLPassthroughBackend
Servers []*ingress.Server
TCPBackends []ingress.L4Service
UDPBackends []ingress.L4Service
HealthzURI string
Cfg Configuration
IsIPV6Enabled bool
IsSSLPassthroughEnabled bool
NginxStatusIpv4Whitelist []string
NginxStatusIpv6Whitelist []string
RedirectServers interface{}
ListenPorts *ListenPorts
PublishService *apiv1.Service
EnableMetrics bool
PID string
StatusSocket string

View file

@ -167,7 +167,7 @@ func (n *NGINXController) syncIngress(interface{}) error {
}
err := wait.ExponentialBackoff(retry, func() (bool, error) {
err := configureDynamically(pcfg)
err := n.configureDynamically(pcfg)
if err == nil {
klog.V(2).Infof("Dynamic reconfiguration succeeded.")
return true, nil
@ -846,9 +846,9 @@ func (n *NGINXController) getServiceClusterEndpoint(svcKey string, backend *netw
// serviceEndpoints returns the upstream servers (Endpoints) associated with a Service.
func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingress.Endpoint, error) {
svc, err := n.store.GetService(svcKey)
var upstreams []ingress.Endpoint
svc, err := n.store.GetService(svcKey)
if err != nil {
return upstreams, err
}
@ -859,14 +859,26 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
if svc.Spec.Type == apiv1.ServiceTypeExternalName {
externalPort, err := strconv.Atoi(backendPort)
if err != nil {
klog.Warningf("Only numeric ports are allowed in ExternalName Services: %q is not a valid port number.", backendPort)
return upstreams, nil
// check if the service ports have one with backendPort as name
found := false
for _, port := range svc.Spec.Ports {
if port.Name == backendPort {
externalPort = int(port.Port)
found = true
break
}
}
if !found {
klog.Errorf("service %v/%v does not have a port with name %v", svc.Namespace, svc.Namespace, backendPort)
return upstreams, nil
}
}
servicePort := apiv1.ServicePort{
Protocol: "TCP",
Port: int32(externalPort),
TargetPort: intstr.FromString(backendPort),
TargetPort: intstr.FromInt(externalPort),
}
endps := getEndpoints(svc, &servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpoints)
if len(endps) == 0 {
@ -897,19 +909,18 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
return upstreams, nil
}
// overridePemFileNameAndPemSHA should only be called when EnableDynamicCertificates
// ideally this function should not exist, the only reason why we use it is that
// we rely on PemFileName in nginx.tmpl to configure SSL directives
// and PemSHA to force reload
func (n *NGINXController) overridePemFileNameAndPemSHA(cert *ingress.SSLCert) {
// TODO(elvinefendi): It is not great but we currently use PemFileName to decide whether SSL needs to be configured
// in nginx configuration or not. The whole thing needs to be refactored, we should rely on a proper
// signal to configure SSL, not PemFileName.
cert.PemFileName = n.cfg.FakeCertificate.PemFileName
func (n *NGINXController) getDefaultSSLCertificate() *ingress.SSLCert {
// read custom default SSL certificate, fall back to generated default certificate
if n.cfg.DefaultSSLCertificate != "" {
certificate, err := n.store.GetLocalSSLCert(n.cfg.DefaultSSLCertificate)
if err == nil {
return certificate
}
// TODO(elvinefendi): This is again another hacky way of avoiding Nginx reload when certificate
// changes in dynamic SSL mode since FakeCertificate never changes.
cert.PemSHA = n.cfg.FakeCertificate.PemSHA
klog.Warningf("Error loading custom default certificate, falling back to generated default:\n%v", err)
}
return n.cfg.FakeCertificate
}
// createServers builds a map of host name to Server structs from a map of
@ -924,42 +935,28 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
bdef := n.store.GetDefaultBackend()
ngxProxy := proxy.Config{
BodySize: bdef.ProxyBodySize,
ConnectTimeout: bdef.ProxyConnectTimeout,
SendTimeout: bdef.ProxySendTimeout,
ReadTimeout: bdef.ProxyReadTimeout,
BuffersNumber: bdef.ProxyBuffersNumber,
BufferSize: bdef.ProxyBufferSize,
CookieDomain: bdef.ProxyCookieDomain,
CookiePath: bdef.ProxyCookiePath,
NextUpstream: bdef.ProxyNextUpstream,
NextUpstreamTimeout: bdef.ProxyNextUpstreamTimeout,
NextUpstreamTries: bdef.ProxyNextUpstreamTries,
RequestBuffering: bdef.ProxyRequestBuffering,
ProxyRedirectFrom: bdef.ProxyRedirectFrom,
ProxyBuffering: bdef.ProxyBuffering,
ProxyHTTPVersion: bdef.ProxyHTTPVersion,
}
defaultCertificate := n.cfg.FakeCertificate
// read custom default SSL certificate, fall back to generated default certificate
if n.cfg.DefaultSSLCertificate != "" {
certificate, err := n.store.GetLocalSSLCert(n.cfg.DefaultSSLCertificate)
if err == nil {
defaultCertificate = certificate
if ngx_config.EnableDynamicCertificates {
n.overridePemFileNameAndPemSHA(defaultCertificate)
}
} else {
klog.Warningf("Error loading custom default certificate, falling back to generated default:\n%v", err)
}
BodySize: bdef.ProxyBodySize,
ConnectTimeout: bdef.ProxyConnectTimeout,
SendTimeout: bdef.ProxySendTimeout,
ReadTimeout: bdef.ProxyReadTimeout,
BuffersNumber: bdef.ProxyBuffersNumber,
BufferSize: bdef.ProxyBufferSize,
CookieDomain: bdef.ProxyCookieDomain,
CookiePath: bdef.ProxyCookiePath,
NextUpstream: bdef.ProxyNextUpstream,
NextUpstreamTimeout: bdef.ProxyNextUpstreamTimeout,
NextUpstreamTries: bdef.ProxyNextUpstreamTries,
RequestBuffering: bdef.ProxyRequestBuffering,
ProxyRedirectFrom: bdef.ProxyRedirectFrom,
ProxyBuffering: bdef.ProxyBuffering,
ProxyHTTPVersion: bdef.ProxyHTTPVersion,
ProxyMaxTempFileSize: bdef.ProxyMaxTempFileSize,
}
// initialize default server and root location
servers[defServerName] = &ingress.Server{
Hostname: defServerName,
SSLCert: *defaultCertificate,
SSLCert: n.getDefaultSSLCertificate(),
Locations: []*ingress.Location{
{
Path: rootLocation,
@ -1023,6 +1020,7 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
if host == "" {
host = defServerName
}
if _, ok := servers[host]; ok {
// server already configured
continue
@ -1090,7 +1088,7 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
}
// only add a certificate if the server does not have one previously configured
if servers[host].SSLCert.PemFileName != "" {
if servers[host].SSLCert != nil {
continue
}
@ -1100,10 +1098,8 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
}
tlsSecretName := extractTLSSecretName(host, ing, n.store.GetLocalSSLCert)
if tlsSecretName == "" {
klog.V(3).Infof("Host %q is listed in the TLS section but secretName is empty. Using default certificate.", host)
servers[host].SSLCert = *defaultCertificate
continue
}
@ -1111,7 +1107,6 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
cert, err := n.store.GetLocalSSLCert(secrKey)
if err != nil {
klog.Warningf("Error getting SSL certificate %q: %v. Using default certificate", secrKey, err)
servers[host].SSLCert = *defaultCertificate
continue
}
@ -1126,16 +1121,11 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
klog.Warningf("SSL certificate %q does not contain a Common Name or Subject Alternative Name for server %q: %v",
secrKey, host, err)
klog.Warningf("Using default certificate")
servers[host].SSLCert = *defaultCertificate
continue
}
}
if ngx_config.EnableDynamicCertificates {
n.overridePemFileNameAndPemSHA(cert)
}
servers[host].SSLCert = *cert
servers[host].SSLCert = cert
if cert.ExpireTime.Before(time.Now().Add(240 * time.Hour)) {
klog.Warningf("SSL certificate for server %q is about to expire (%v)", host, cert.ExpireTime)
@ -1176,9 +1166,11 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress)
loc.InfluxDB = anns.InfluxDB
loc.DefaultBackend = anns.DefaultBackend
loc.BackendProtocol = anns.BackendProtocol
loc.FastCGI = anns.FastCGI
loc.CustomHTTPErrors = anns.CustomHTTPErrors
loc.ModSecurity = anns.ModSecurity
loc.Satisfy = anns.Satisfy
loc.Mirror = anns.Mirror
}
// OK to merge canary ingresses iff there exists one or more ingresses to potentially merge into

View file

@ -37,7 +37,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/ingress-nginx/internal/file"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations"
"k8s.io/ingress-nginx/internal/ingress/annotations/canary"
@ -1109,11 +1108,6 @@ func newNGINXController(t *testing.T) *NGINXController {
t.Fatalf("error creating the configuration map: %v", err)
}
fs, err := file.NewFakeFS()
if err != nil {
t.Fatalf("error: %v", err)
}
storer := store.New(
ns,
fmt.Sprintf("%v/config", ns),
@ -1122,12 +1116,11 @@ func newNGINXController(t *testing.T) *NGINXController {
"",
10*time.Minute,
clientSet,
fs,
channels.NewRingChannel(10),
pod,
false)
sslCert := ssl.GetFakeSSLCert(fs)
sslCert := ssl.GetFakeSSLCert()
config := &Configuration{
FakeCertificate: sslCert,
ListenPorts: &ngx_config.ListenPorts{
@ -1136,10 +1129,9 @@ func newNGINXController(t *testing.T) *NGINXController {
}
return &NGINXController{
store: storer,
cfg: config,
command: NewNginxCommand(),
fileSystem: fs,
store: storer,
cfg: config,
command: NewNginxCommand(),
}
}

View file

@ -27,6 +27,7 @@ import (
"os"
"os/exec"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
@ -44,7 +45,6 @@ import (
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/util/filesystem"
adm_controler "k8s.io/ingress-nginx/internal/admission/controller"
"k8s.io/ingress-nginx/internal/file"
@ -69,12 +69,8 @@ const (
tempNginxPattern = "nginx-cfg"
)
var (
tmplPath = "/etc/nginx/template/nginx.tmpl"
)
// NewNGINXController creates a new NGINX Ingress controller.
func NewNGINXController(config *Configuration, mc metric.Collector, fs file.Filesystem) *NGINXController {
func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXController {
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(klog.Infof)
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{
@ -102,8 +98,6 @@ func NewNGINXController(config *Configuration, mc metric.Collector, fs file.File
stopLock: &sync.Mutex{},
fileSystem: fs,
runningConfig: new(ingress.Configuration),
Proxy: &TCPProxy{},
@ -135,7 +129,6 @@ func NewNGINXController(config *Configuration, mc metric.Collector, fs file.File
config.DefaultSSLCertificate,
config.ResyncPeriod,
config.Client,
fs,
n.updateCh,
pod,
config.DisableCatchAll)
@ -156,7 +149,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector, fs file.File
}
onTemplateChange := func() {
template, err := ngx_template.NewTemplate(tmplPath, fs)
template, err := ngx_template.NewTemplate(nginx.TemplatePath)
if err != nil {
// this error is different from the rest because it must be clear why nginx is not working
klog.Errorf(`
@ -172,21 +165,16 @@ Error loading new template: %v
n.syncQueue.EnqueueTask(task.GetDummyObject("template-change"))
}
ngxTpl, err := ngx_template.NewTemplate(tmplPath, fs)
ngxTpl, err := ngx_template.NewTemplate(nginx.TemplatePath)
if err != nil {
klog.Fatalf("Invalid NGINX configuration template: %v", err)
}
n.t = ngxTpl
if _, ok := fs.(filesystem.DefaultFs); !ok {
// do not setup watchers on tests
return n
}
_, err = watch.NewFileWatcher(tmplPath, onTemplateChange)
_, err = watch.NewFileWatcher(nginx.TemplatePath, onTemplateChange)
if err != nil {
klog.Fatalf("Error creating file watcher for %v: %v", tmplPath, err)
klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, err)
}
filesToWatch := []string{}
@ -260,8 +248,6 @@ type NGINXController struct {
store store.Storer
fileSystem filesystem.Filesystem
metricCollector metric.Collector
validationWebhookServer *http.Server
@ -582,7 +568,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
nsSecName := strings.Replace(secretName, "/", "-", -1)
dh, ok := secret.Data["dhparam.pem"]
if ok {
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh, n.fileSystem)
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh)
if err != nil {
klog.Warningf("Error adding or updating dhparam file %v: %v", nsSecName, err)
} else {
@ -594,25 +580,26 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
cfg.SSLDHParam = sslDHParam
cfg.DefaultSSLCertificate = n.getDefaultSSLCertificate()
tc := ngx_config.TemplateConfig{
ProxySetHeaders: setHeaders,
AddHeaders: addHeaders,
BacklogSize: sysctlSomaxconn(),
Backends: ingressCfg.Backends,
PassthroughBackends: ingressCfg.PassthroughBackends,
Servers: ingressCfg.Servers,
TCPBackends: ingressCfg.TCPEndpoints,
UDPBackends: ingressCfg.UDPEndpoints,
Cfg: cfg,
IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6,
NginxStatusIpv4Whitelist: cfg.NginxStatusIpv4Whitelist,
NginxStatusIpv6Whitelist: cfg.NginxStatusIpv6Whitelist,
RedirectServers: buildRedirects(ingressCfg.Servers),
IsSSLPassthroughEnabled: n.cfg.EnableSSLPassthrough,
ListenPorts: n.cfg.ListenPorts,
PublishService: n.GetPublishService(),
EnableDynamicCertificates: ngx_config.EnableDynamicCertificates,
EnableMetrics: n.cfg.EnableMetrics,
ProxySetHeaders: setHeaders,
AddHeaders: addHeaders,
BacklogSize: sysctlSomaxconn(),
Backends: ingressCfg.Backends,
PassthroughBackends: ingressCfg.PassthroughBackends,
Servers: ingressCfg.Servers,
TCPBackends: ingressCfg.TCPEndpoints,
UDPBackends: ingressCfg.UDPEndpoints,
Cfg: cfg,
IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6,
NginxStatusIpv4Whitelist: cfg.NginxStatusIpv4Whitelist,
NginxStatusIpv6Whitelist: cfg.NginxStatusIpv6Whitelist,
RedirectServers: buildRedirects(ingressCfg.Servers),
IsSSLPassthroughEnabled: n.cfg.EnableSSLPassthrough,
ListenPorts: n.cfg.ListenPorts,
PublishService: n.GetPublishService(),
EnableMetrics: n.cfg.EnableMetrics,
HealthzURI: nginx.HealthPath,
PID: nginx.PID,
@ -698,7 +685,12 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error {
diffOutput, err := exec.Command("diff", "-u", cfgPath, tmpfile.Name()).CombinedOutput()
if err != nil {
klog.Warningf("Failed to executing diff command: %v", err)
if exitError, ok := err.(*exec.ExitError); ok {
ws := exitError.Sys().(syscall.WaitStatus)
if ws.ExitStatus() == 2 {
klog.Warningf("Failed to executing diff command: %v", err)
}
}
}
klog.Infof("NGINX configuration diff:\n%v", string(diffOutput))
@ -800,7 +792,9 @@ func clearCertificates(config *ingress.Configuration) {
var clearedServers []*ingress.Server
for _, server := range config.Servers {
copyOfServer := *server
copyOfServer.SSLCert = ingress.SSLCert{PemFileName: copyOfServer.SSLCert.PemFileName}
if copyOfServer.SSLCert != nil {
copyOfServer.SSLCert = &ingress.SSLCert{PemFileName: copyOfServer.SSLCert.PemFileName}
}
clearedServers = append(clearedServers, &copyOfServer)
}
config.Servers = clearedServers
@ -848,20 +842,112 @@ func (n *NGINXController) IsDynamicConfigurationEnough(pcfg *ingress.Configurati
copyOfRunningConfig.ControllerPodsCount = 0
copyOfPcfg.ControllerPodsCount = 0
if ngx_config.EnableDynamicCertificates {
clearCertificates(&copyOfRunningConfig)
clearCertificates(&copyOfPcfg)
}
clearCertificates(&copyOfRunningConfig)
clearCertificates(&copyOfPcfg)
return copyOfRunningConfig.Equal(&copyOfPcfg)
}
// configureDynamically encodes new Backends in JSON format and POSTs the
// payload to an internal HTTP endpoint handled by Lua.
func configureDynamically(pcfg *ingress.Configuration) error {
backends := make([]*ingress.Backend, len(pcfg.Backends))
func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) error {
backendsChanged := !reflect.DeepEqual(n.runningConfig.Backends, pcfg.Backends)
if backendsChanged {
err := configureBackends(pcfg.Backends)
if err != nil {
return err
}
}
for i, backend := range pcfg.Backends {
streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints)
if streamConfigurationChanged {
err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints)
if err != nil {
return err
}
}
if n.runningConfig.ControllerPodsCount != pcfg.ControllerPodsCount {
statusCode, _, err := nginx.NewPostStatusRequest("/configuration/general", "application/json", ingress.GeneralConfig{
ControllerPodsCount: pcfg.ControllerPodsCount,
})
if err != nil {
return err
}
if statusCode != http.StatusCreated {
return fmt.Errorf("unexpected error code: %d", statusCode)
}
}
serversChanged := !reflect.DeepEqual(n.runningConfig.Servers, pcfg.Servers)
if serversChanged {
err := configureCertificates(pcfg.Servers)
if err != nil {
return err
}
}
return nil
}
func updateStreamConfiguration(TCPEndpoints []ingress.L4Service, UDPEndpoints []ingress.L4Service) error {
streams := make([]ingress.Backend, 0)
for _, ep := range TCPEndpoints {
var service *apiv1.Service
if ep.Service != nil {
service = &apiv1.Service{Spec: ep.Service.Spec}
}
key := fmt.Sprintf("tcp-%v-%v-%v", ep.Backend.Namespace, ep.Backend.Name, ep.Backend.Port.String())
streams = append(streams, ingress.Backend{
Name: key,
Endpoints: ep.Endpoints,
Port: intstr.FromInt(ep.Port),
Service: service,
})
}
for _, ep := range UDPEndpoints {
var service *apiv1.Service
if ep.Service != nil {
service = &apiv1.Service{Spec: ep.Service.Spec}
}
key := fmt.Sprintf("udp-%v-%v-%v", ep.Backend.Namespace, ep.Backend.Name, ep.Backend.Port.String())
streams = append(streams, ingress.Backend{
Name: key,
Endpoints: ep.Endpoints,
Port: intstr.FromInt(ep.Port),
Service: service,
})
}
conn, err := net.Dial("unix", nginx.StreamSocket)
if err != nil {
return err
}
defer conn.Close()
buf, err := json.Marshal(streams)
if err != nil {
return err
}
_, err = conn.Write(buf)
if err != nil {
return err
}
_, err = fmt.Fprintf(conn, "\r\n")
if err != nil {
return err
}
return nil
}
func configureBackends(rawBackends []*ingress.Backend) error {
backends := make([]*ingress.Backend, len(rawBackends))
for i, backend := range rawBackends {
var service *apiv1.Service
if backend.Service != nil {
service = &apiv1.Service{Spec: backend.Service.Spec}
@ -900,99 +986,22 @@ func configureDynamically(pcfg *ingress.Configuration) error {
return fmt.Errorf("unexpected error code: %d", statusCode)
}
streams := make([]ingress.Backend, 0)
for _, ep := range pcfg.TCPEndpoints {
var service *apiv1.Service
if ep.Service != nil {
service = &apiv1.Service{Spec: ep.Service.Spec}
}
key := fmt.Sprintf("tcp-%v-%v-%v", ep.Backend.Namespace, ep.Backend.Name, ep.Backend.Port.String())
streams = append(streams, ingress.Backend{
Name: key,
Endpoints: ep.Endpoints,
Port: intstr.FromInt(ep.Port),
Service: service,
})
}
for _, ep := range pcfg.UDPEndpoints {
var service *apiv1.Service
if ep.Service != nil {
service = &apiv1.Service{Spec: ep.Service.Spec}
}
key := fmt.Sprintf("udp-%v-%v-%v", ep.Backend.Namespace, ep.Backend.Name, ep.Backend.Port.String())
streams = append(streams, ingress.Backend{
Name: key,
Endpoints: ep.Endpoints,
Port: intstr.FromInt(ep.Port),
Service: service,
})
}
err = updateStreamConfiguration(streams)
if err != nil {
return err
}
statusCode, _, err = nginx.NewPostStatusRequest("/configuration/general", "application/json", ingress.GeneralConfig{
ControllerPodsCount: pcfg.ControllerPodsCount,
})
if err != nil {
return err
}
if statusCode != http.StatusCreated {
return fmt.Errorf("unexpected error code: %d", statusCode)
}
if ngx_config.EnableDynamicCertificates {
err = configureCertificates(pcfg)
if err != nil {
return err
}
}
return nil
}
func updateStreamConfiguration(streams []ingress.Backend) error {
conn, err := net.Dial("unix", nginx.StreamSocket)
if err != nil {
return err
}
defer conn.Close()
buf, err := json.Marshal(streams)
if err != nil {
return err
}
_, err = conn.Write(buf)
if err != nil {
return err
}
_, err = fmt.Fprintf(conn, "\r\n")
if err != nil {
return err
}
return nil
}
// configureCertificates JSON encodes certificates and POSTs it to an internal HTTP endpoint
// that is handled by Lua
func configureCertificates(pcfg *ingress.Configuration) error {
var servers []*ingress.Server
func configureCertificates(rawServers []*ingress.Server) error {
servers := make([]*ingress.Server, 0)
for _, server := range pcfg.Servers {
if server.SSLCert.PemCertKey == "" {
for _, server := range rawServers {
if server.SSLCert == nil {
continue
}
servers = append(servers, &ingress.Server{
Hostname: server.Hostname,
SSLCert: ingress.SSLCert{
SSLCert: &ingress.SSLCert{
PemCertKey: server.SSLCert.PemCertKey,
},
})
@ -1000,22 +1009,22 @@ func configureCertificates(pcfg *ingress.Configuration) error {
if server.Alias != "" && ssl.IsValidHostname(server.Alias, server.SSLCert.CN) {
servers = append(servers, &ingress.Server{
Hostname: server.Alias,
SSLCert: ingress.SSLCert{
SSLCert: &ingress.SSLCert{
PemCertKey: server.SSLCert.PemCertKey,
},
})
}
}
redirects := buildRedirects(pcfg.Servers)
redirects := buildRedirects(rawServers)
for _, redirect := range redirects {
if redirect.SSLCert.PemCertKey == "" {
if redirect.SSLCert == nil {
continue
}
servers = append(servers, &ingress.Server{
Hostname: redirect.From,
SSLCert: ingress.SSLCert{
SSLCert: &ingress.SSLCert{
PemCertKey: redirect.SSLCert.PemCertKey,
},
})
@ -1126,7 +1135,7 @@ func cleanTempNginxCfg() error {
type redirect struct {
From string
To string
SSLCert ingress.SSLCert
SSLCert *ingress.SSLCert
}
func buildRedirects(servers []*ingress.Server) []*redirect {
@ -1170,7 +1179,7 @@ func buildRedirects(servers []*ingress.Server) []*redirect {
To: to,
}
if srv.SSLCert.PemSHA != "" {
if srv.SSLCert != nil {
if ssl.IsValidHostname(from, srv.SSLCert.CN) {
r.SSLCert = srv.SSLCert
} else {

View file

@ -32,14 +32,10 @@ import (
apiv1 "k8s.io/api/core/v1"
"k8s.io/ingress-nginx/internal/ingress"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/nginx"
)
func TestIsDynamicConfigurationEnough(t *testing.T) {
ngx_config.EnableDynamicCertificates = false
defer func() { ngx_config.EnableDynamicCertificates = true }()
backends := []*ingress.Backend{{
Name: "fakenamespace-myapp-80",
Endpoints: []ingress.Endpoint{
@ -62,7 +58,7 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
Backend: "fakenamespace-myapp-80",
},
},
SSLCert: ingress.SSLCert{
SSLCert: &ingress.SSLCert{
PemCertKey: "fake-certificate",
},
}}
@ -98,8 +94,6 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
Servers: servers,
}
ngx_config.EnableDynamicCertificates = true
if !n.IsDynamicConfigurationEnough(newConfig) {
t.Errorf("Expected to be dynamically configurable when only backends change")
}
@ -112,7 +106,7 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
Backend: "fakenamespace-myapp-80",
},
},
SSLCert: ingress.SSLCert{
SSLCert: &ingress.SSLCert{
PemCertKey: "fake-certificate",
},
}}
@ -168,6 +162,13 @@ func TestConfigureDynamically(t *testing.T) {
defer streamListener.Close()
defer os.Remove(nginx.StreamSocket)
endpointStats := map[string]int{"/configuration/backends": 0, "/configuration/general": 0, "/configuration/servers": 0}
resetEndpointStats := func() {
for k := range endpointStats {
endpointStats[k] = 0
}
}
server := &httptest.Server{
Listener: listener,
Config: &http.Server{
@ -184,6 +185,8 @@ func TestConfigureDynamically(t *testing.T) {
}
body := string(b)
endpointStats[r.URL.Path] += 1
switch r.URL.Path {
case "/configuration/backends":
{
@ -201,6 +204,12 @@ func TestConfigureDynamically(t *testing.T) {
t.Errorf("controllerPodsCount should be present in JSON content: %v", body)
}
}
case "/configuration/servers":
{
if !strings.Contains(body, "[]") {
t.Errorf("controllerPodsCount should be present in JSON content: %v", body)
}
}
default:
t.Errorf("unknown request to %s", r.URL.Path)
}
@ -246,17 +255,67 @@ func TestConfigureDynamically(t *testing.T) {
ControllerPodsCount: 2,
}
ngx_config.EnableDynamicCertificates = false
defer func() { ngx_config.EnableDynamicCertificates = true }()
n := &NGINXController{
runningConfig: &ingress.Configuration{},
cfg: &Configuration{},
}
err = configureDynamically(commonConfig)
err = n.configureDynamically(commonConfig)
if err != nil {
t.Errorf("unexpected error posting dynamic configuration: %v", err)
}
if commonConfig.Backends[0].Endpoints[0].Target != target {
t.Errorf("unexpected change in the configuration object after configureDynamically invocation")
}
for endpoint, count := range endpointStats {
if count != 1 {
t.Errorf("Expected %v to receive %d requests but received %d.", endpoint, 1, count)
}
}
resetEndpointStats()
n.runningConfig.Backends = backends
err = n.configureDynamically(commonConfig)
if err != nil {
t.Errorf("unexpected error posting dynamic configuration: %v", err)
}
for endpoint, count := range endpointStats {
if endpoint == "/configuration/backends" {
if count != 0 {
t.Errorf("Expected %v to receive %d requests but received %d.", endpoint, 0, count)
}
} else if count != 1 {
t.Errorf("Expected %v to receive %d requests but received %d.", endpoint, 1, count)
}
}
resetEndpointStats()
n.runningConfig.Servers = servers
err = n.configureDynamically(commonConfig)
if err != nil {
t.Errorf("unexpected error posting dynamic configuration: %v", err)
}
if count, _ := endpointStats["/configuration/backends"]; count != 0 {
t.Errorf("Expected %v to receive %d requests but received %d.", "/configuration/backends", 0, count)
}
if count, _ := endpointStats["/configuration/servers"]; count != 0 {
t.Errorf("Expected %v to receive %d requests but received %d.", "/configuration/servers", 0, count)
}
if count, _ := endpointStats["/configuration/general"]; count != 1 {
t.Errorf("Expected %v to receive %d requests but received %d.", "/configuration/general", 0, count)
}
resetEndpointStats()
n.runningConfig.ControllerPodsCount = commonConfig.ControllerPodsCount
err = n.configureDynamically(commonConfig)
if err != nil {
t.Errorf("unexpected error posting dynamic configuration: %v", err)
}
for endpoint, count := range endpointStats {
if count != 0 {
t.Errorf("Expected %v to receive %d requests but received %d.", endpoint, 0, count)
}
}
}
func TestConfigureCertificates(t *testing.T) {
@ -276,7 +335,7 @@ func TestConfigureCertificates(t *testing.T) {
servers := []*ingress.Server{{
Hostname: "myapp.fake",
SSLCert: ingress.SSLCert{
SSLCert: &ingress.SSLCert{
PemCertKey: "fake-cert",
},
}}
@ -316,11 +375,7 @@ func TestConfigureCertificates(t *testing.T) {
defer server.Close()
server.Start()
commonConfig := &ingress.Configuration{
Servers: servers,
}
err = configureCertificates(commonConfig)
err = configureCertificates(servers)
if err != nil {
t.Errorf("unexpected error posting dynamic certificate configuration: %v", err)
}

View file

@ -17,17 +17,20 @@ limitations under the License.
package store
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"strings"
"k8s.io/klog"
"github.com/pkg/errors"
apiv1 "k8s.io/api/core/v1"
networking "k8s.io/api/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/internal/file"
"k8s.io/ingress-nginx/internal/ingress"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/net/ssl"
)
@ -39,7 +42,6 @@ func (s *k8sStore) syncSecret(key string) {
klog.V(3).Infof("Syncing Secret %q", key)
// TODO: getPemCertificate should not write to disk to avoid unnecessary overhead
cert, err := s.getPemCertificate(key)
if err != nil {
if !isErrSecretForAuth(err) {
@ -92,6 +94,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
if cert == nil {
return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName)
}
if key == nil {
return nil, fmt.Errorf("key 'tls.key' missing from Secret %q", secretName)
}
@ -101,15 +104,16 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
}
if !ngx_config.EnableDynamicCertificates || len(ca) > 0 {
err = ssl.StoreSSLCertOnDisk(s.filesystem, nsSecName, sslCert)
if len(ca) > 0 {
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)
if err != nil {
return nil, fmt.Errorf("error while storing certificate and key: %v", err)
}
}
if len(ca) > 0 {
err = ssl.ConfigureCACertWithCertAndKey(s.filesystem, nsSecName, ca, sslCert)
sslCert.CAFileName = path
sslCert.CASHA = file.SHA1(path)
err = ssl.ConfigureCACertWithCertAndKey(nsSecName, ca, sslCert)
if err != nil {
return nil, fmt.Errorf("error configuring CA certificate: %v", err)
}
@ -120,14 +124,13 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
msg += " and authentication"
}
klog.V(3).Info(msg)
} else if len(ca) > 0 {
sslCert, err = ssl.CreateCACert(ca)
if err != nil {
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
}
err = ssl.ConfigureCACert(s.filesystem, nsSecName, ca, sslCert)
err = ssl.ConfigureCACert(nsSecName, ca, sslCert)
if err != nil {
return nil, fmt.Errorf("error configuring CA certificate: %v", err)
}
@ -135,7 +138,6 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
// makes this secret in 'syncSecret' to be used for Certificate Authentication
// this does not enable Certificate Authentication
klog.V(3).Infof("Configuring Secret %q for TLS authentication", secretName)
} else {
if auth != nil {
return nil, ErrSecretForAuth
@ -147,6 +149,21 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
sslCert.Name = secret.Name
sslCert.Namespace = secret.Namespace
hasher := sha1.New()
hasher.Write(sslCert.Certificate.Raw)
sslCert.PemSHA = hex.EncodeToString(hasher.Sum(nil))
// the default SSL certificate needs to be present on disk
if secretName == s.defaultSSLCertificate {
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)
if err != nil {
return nil, errors.Wrap(err, "storing default SSL Certificate")
}
sslCert.PemFileName = path
}
return sslCert, nil
}

View file

@ -152,9 +152,9 @@ func (e NotExistsError) Error() string {
// Run initiates the synchronization of the informers against the API server.
func (i *Informer) Run(stopCh chan struct{}) {
go i.Secret.Run(stopCh)
go i.Endpoint.Run(stopCh)
go i.Service.Run(stopCh)
go i.Secret.Run(stopCh)
go i.ConfigMap.Run(stopCh)
go i.Pod.Run(stopCh)
@ -165,6 +165,7 @@ func (i *Informer) Run(stopCh chan struct{}) {
i.Service.HasSynced,
i.Secret.HasSynced,
i.ConfigMap.HasSynced,
i.Pod.HasSynced,
) {
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
}
@ -208,8 +209,6 @@ type k8sStore struct {
// secret in the annotations.
secretIngressMap ObjectRefMap
filesystem file.Filesystem
// updateCh
updateCh *channels.RingChannel
@ -229,7 +228,6 @@ func New(
namespace, configmap, tcp, udp, defaultSSLCertificate string,
resyncPeriod time.Duration,
client clientset.Interface,
fs file.Filesystem,
updateCh *channels.RingChannel,
pod *k8s.PodInfo,
disableCatchAll bool) Storer {
@ -238,7 +236,6 @@ func New(
informers: &Informer{},
listers: &Lister{},
sslStore: NewSSLCertTracker(),
filesystem: fs,
updateCh: updateCh,
backendConfig: ngx_config.NewDefault(),
syncSecretMu: &sync.Mutex{},
@ -494,6 +491,7 @@ func New(
}
store.syncIngress(ing)
}
updateCh.In() <- Event{
Type: DeleteEvent,
Obj: obj,
@ -814,7 +812,7 @@ func (s *k8sStore) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error
return &resolver.AuthSSLCert{
Secret: name,
CAFileName: cert.CAFileName,
PemSHA: cert.PemSHA,
CASHA: cert.CASHA,
}, nil
}

View file

@ -38,10 +38,8 @@ import (
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"k8s.io/ingress-nginx/internal/file"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/test/e2e/framework"
)
@ -87,7 +85,6 @@ func TestStore(t *testing.T) {
}
}(updateCh)
fs := newFS(t)
storer := New(
ns,
fmt.Sprintf("%v/config", ns),
@ -96,7 +93,6 @@ func TestStore(t *testing.T) {
"",
10*time.Minute,
clientSet,
fs,
updateCh,
pod,
false)
@ -167,7 +163,6 @@ func TestStore(t *testing.T) {
}
}(updateCh)
fs := newFS(t)
storer := New(
ns,
fmt.Sprintf("%v/config", ns),
@ -176,7 +171,6 @@ func TestStore(t *testing.T) {
"",
10*time.Minute,
clientSet,
fs,
updateCh,
pod,
false)
@ -317,7 +311,6 @@ func TestStore(t *testing.T) {
}
}(updateCh)
fs := newFS(t)
storer := New(
ns,
fmt.Sprintf("%v/config", ns),
@ -326,7 +319,6 @@ func TestStore(t *testing.T) {
"",
10*time.Minute,
clientSet,
fs,
updateCh,
pod,
false)
@ -423,7 +415,6 @@ func TestStore(t *testing.T) {
}
}(updateCh)
fs := newFS(t)
storer := New(
ns,
fmt.Sprintf("%v/config", ns),
@ -432,7 +423,6 @@ func TestStore(t *testing.T) {
"",
10*time.Minute,
clientSet,
fs,
updateCh,
pod,
false)
@ -512,7 +502,6 @@ func TestStore(t *testing.T) {
}
}(updateCh)
fs := newFS(t)
storer := New(
ns,
fmt.Sprintf("%v/config", ns),
@ -521,7 +510,6 @@ func TestStore(t *testing.T) {
"",
10*time.Minute,
clientSet,
fs,
updateCh,
pod,
false)
@ -623,7 +611,6 @@ func TestStore(t *testing.T) {
}
}(updateCh)
fs := newFS(t)
storer := New(
ns,
fmt.Sprintf("%v/config", ns),
@ -632,7 +619,6 @@ func TestStore(t *testing.T) {
"",
10*time.Minute,
clientSet,
fs,
updateCh,
pod,
false)
@ -701,39 +687,6 @@ func TestStore(t *testing.T) {
if err != nil {
t.Errorf("error creating secret: %v", err)
}
t.Run("should exists a secret in the local store and filesystem", func(t *testing.T) {
ngx_config.EnableDynamicCertificates = false
defer func() { ngx_config.EnableDynamicCertificates = true }()
err := framework.WaitForSecretInNamespace(clientSet, ns, name)
if err != nil {
t.Errorf("error waiting for secret: %v", err)
}
time.Sleep(5 * time.Second)
pemFile := fmt.Sprintf("%v/%v-%v.pem", file.DefaultSSLDirectory, ns, name)
err = framework.WaitForFileInFS(pemFile, fs)
if err != nil {
t.Errorf("error waiting for file to exist on the file system: %v", err)
}
secretName := fmt.Sprintf("%v/%v", ns, name)
sslCert, err := storer.GetLocalSSLCert(secretName)
if err != nil {
t.Errorf("error reading local secret %v: %v", secretName, err)
}
if sslCert == nil {
t.Errorf("expected a secret but none returned")
}
pemSHA := file.SHA1(pemFile)
if sslCert.PemSHA != pemSHA {
t.Errorf("SHA of secret on disk differs from local secret store (%v != %v)", pemSHA, sslCert.PemSHA)
}
})
})
// test add ingress with secret it doesn't exists and then add secret
@ -821,22 +774,9 @@ func deleteIngress(ingress *networking.Ingress, clientSet kubernetes.Interface,
t.Logf("Ingress %+v deleted", ingress)
}
func newFS(t *testing.T) file.Filesystem {
fs, err := file.NewFakeFS()
if err != nil {
t.Fatalf("error creating filesystem: %v", err)
}
return fs
}
// newStore creates a new mock object store for tests which do not require the
// use of Informers.
func newStore(t *testing.T) *k8sStore {
fs, err := file.NewFakeFS()
if err != nil {
t.Fatalf("error: %v", err)
}
pod := &k8s.PodInfo{
Name: "ingress-1",
Namespace: v1.NamespaceDefault,
@ -853,7 +793,6 @@ func newStore(t *testing.T) *k8sStore {
Pod: PodLister{cache.NewStore(cache.MetaNamespaceKeyFunc)},
},
sslStore: NewSSLCertTracker(),
filesystem: fs,
updateCh: channels.NewRingChannel(10),
syncSecretMu: new(sync.Mutex),
backendConfigMu: new(sync.RWMutex),

View file

@ -59,10 +59,23 @@ const (
globalAuthSnippet = "global-auth-snippet"
globalAuthCacheKey = "global-auth-cache-key"
globalAuthCacheDuration = "global-auth-cache-duration"
luaSharedDictsKey = "lua-shared-dicts"
)
var (
validRedirectCodes = sets.NewInt([]int{301, 302, 307, 308}...)
validRedirectCodes = sets.NewInt([]int{301, 302, 307, 308}...)
defaultLuaSharedDicts = map[string]int{
"configuration_data": 20,
"certificate_data": 20,
"balancer_ewma": 10,
"balancer_ewma_last_touched_at": 10,
"balancer_ewma_locks": 1,
}
)
const (
maxAllowedLuaDictSize = 200
maxNumberOfLuaDicts = 100
)
// ReadConfig obtains the configuration defined by the user merged with the defaults.
@ -87,7 +100,40 @@ func ReadConfig(src map[string]string) config.Configuration {
blockUserAgentList := make([]string, 0)
blockRefererList := make([]string, 0)
responseHeaders := make([]string, 0)
luaSharedDicts := make(map[string]int)
//parse lua shared dict values
if val, ok := conf[luaSharedDictsKey]; ok {
delete(conf, luaSharedDictsKey)
lsd := strings.Split(val, ",")
for _, v := range lsd {
v = strings.Replace(v, " ", "", -1)
results := strings.SplitN(v, ":", 2)
dictName := results[0]
size, err := strconv.Atoi(results[1])
if err != nil {
klog.Errorf("Ignoring non integer value %v for Lua dictionary %v: %v.", results[1], dictName, err)
continue
}
if size > maxAllowedLuaDictSize {
klog.Errorf("Ignoring %v for Lua dictionary %v: maximum size is %v.", size, dictName, maxAllowedLuaDictSize)
continue
}
if len(luaSharedDicts)+1 > maxNumberOfLuaDicts {
klog.Errorf("Ignoring %v for Lua dictionary %v: can not configure more than %v dictionaries.",
size, dictName, maxNumberOfLuaDicts)
continue
}
luaSharedDicts[dictName] = size
}
}
// set default Lua shared dicts
for k, v := range defaultLuaSharedDicts {
if _, ok := luaSharedDicts[k]; !ok {
luaSharedDicts[k] = v
}
}
if val, ok := conf[customHTTPErrors]; ok {
delete(conf, customHTTPErrors)
for _, i := range strings.Split(val, ",") {
@ -305,6 +351,7 @@ func ReadConfig(src map[string]string) config.Configuration {
to.HideHeaders = hideHeadersList
to.ProxyStreamResponses = streamResponses
to.DisableIpv6DNS = !ing_net.IsIPv6Enabled()
to.LuaSharedDicts = luaSharedDicts
config := &mapstructure.DecoderConfig{
Metadata: nil,

View file

@ -64,7 +64,6 @@ func TestMergeConfigMapToStruct(t *testing.T) {
"access-log-path": "/var/log/test/access.log",
"error-log-path": "/var/log/test/error.log",
"use-gzip": "true",
"enable-dynamic-tls-records": "false",
"gzip-level": "9",
"gzip-types": "text/html",
"proxy-real-ip-cidr": "1.1.1.1/8,2.2.2.2/24",
@ -84,7 +83,6 @@ func TestMergeConfigMapToStruct(t *testing.T) {
def.SkipAccessLogURLs = []string{"/log", "/demo", "/test"}
def.ProxyReadTimeout = 1
def.ProxySendTimeout = 2
def.EnableDynamicTLSRecords = false
def.UseProxyProtocol = true
def.GzipLevel = 9
def.GzipTypes = "text/html"
@ -95,7 +93,7 @@ func TestMergeConfigMapToStruct(t *testing.T) {
def.NginxStatusIpv4Whitelist = []string{"127.0.0.1", "10.0.0.0/24"}
def.NginxStatusIpv6Whitelist = []string{"::1", "2001::/16"}
def.ProxyAddOriginalURIHeader = false
def.LuaSharedDicts = defaultLuaSharedDicts
def.DisableIpv6DNS = true
hash, err := hashstructure.Hash(def, &hashstructure.HashOptions{
@ -125,6 +123,7 @@ func TestMergeConfigMapToStruct(t *testing.T) {
}
def = config.NewDefault()
def.LuaSharedDicts = defaultLuaSharedDicts
def.DisableIpv6DNS = true
hash, err = hashstructure.Hash(def, &hashstructure.HashOptions{
@ -143,6 +142,7 @@ func TestMergeConfigMapToStruct(t *testing.T) {
}
def = config.NewDefault()
def.LuaSharedDicts = defaultLuaSharedDicts
def.WhitelistSourceRange = []string{"1.1.1.1/32"}
def.DisableIpv6DNS = true
@ -303,3 +303,56 @@ func TestGlobalExternalAuthCacheDurationParsing(t *testing.T) {
}
}
}
func TestLuaSharedDictsParsing(t *testing.T) {
testsCases := []struct {
name string
entry map[string]string
expect map[string]int
}{
{
name: "default dicts configured when lua-shared-dicts is not set",
entry: make(map[string]string),
expect: defaultLuaSharedDicts,
},
{
name: "configuration_data only",
entry: map[string]string{"lua-shared-dicts": "configuration_data:5"},
expect: map[string]int{"configuration_data": 5},
},
{
name: "certificate_data only",
entry: map[string]string{"lua-shared-dicts": "certificate_data: 4"},
expect: map[string]int{"certificate_data": 4},
},
{
name: "custom dicts",
entry: map[string]string{"lua-shared-dicts": "configuration_data: 10, my_random_dict:15 , another_example:2"},
expect: map[string]int{"configuration_data": 10, "my_random_dict": 15, "another_example": 2},
},
{
name: "invalid size value should be ignored",
entry: map[string]string{"lua-shared-dicts": "mydict: 10, invalid_dict: 1a"},
expect: map[string]int{"mydict": 10},
},
{
name: "dictionary size can not be larger than 200",
entry: map[string]string{"lua-shared-dicts": "mydict: 10, invalid_dict: 201"},
expect: map[string]int{"mydict": 10},
},
}
for _, tc := range testsCases {
// dynamically insert default dicts in the expected output
for dictName, dictSize := range defaultLuaSharedDicts {
if _, ok := tc.expect[dictName]; !ok {
tc.expect[dictName] = dictSize
}
}
cfg := ReadConfig(tc.entry)
if !reflect.DeepEqual(cfg.LuaSharedDicts, tc.expect) {
t.Errorf("Testing %v. Expected \"%v\" but \"%v\" was returned", tc.name, tc.expect, cfg.LuaSharedDicts)
}
}
}

View file

@ -21,6 +21,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/url"
@ -37,13 +38,13 @@ import (
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/ingress-nginx/internal/file"
"k8s.io/klog"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
"k8s.io/ingress-nginx/internal/ingress/controller/config"
ing_net "k8s.io/ingress-nginx/internal/net"
"k8s.io/klog"
)
const (
@ -66,8 +67,8 @@ type Template struct {
//NewTemplate returns a new Template instance or an
//error if the specified template file contains errors
func NewTemplate(file string, fs file.Filesystem) (*Template, error) {
data, err := fs.ReadFile(file)
func NewTemplate(file string) (*Template, error) {
data, err := ioutil.ReadFile(file)
if err != nil {
return nil, errors.Wrapf(err, "unexpected error reading template %v", file)
}
@ -132,36 +133,37 @@ var (
}
return true
},
"escapeLiteralDollar": escapeLiteralDollar,
"shouldConfigureLuaRestyWAF": shouldConfigureLuaRestyWAF,
"buildLuaSharedDictionaries": buildLuaSharedDictionaries,
"buildLocation": buildLocation,
"buildAuthLocation": buildAuthLocation,
"shouldApplyGlobalAuth": shouldApplyGlobalAuth,
"buildAuthResponseHeaders": buildAuthResponseHeaders,
"buildProxyPass": buildProxyPass,
"filterRateLimits": filterRateLimits,
"buildRateLimitZones": buildRateLimitZones,
"buildRateLimit": buildRateLimit,
"buildResolversForLua": buildResolversForLua,
"configForLua": configForLua,
"locationConfigForLua": locationConfigForLua,
"buildResolvers": buildResolvers,
"buildUpstreamName": buildUpstreamName,
"isLocationInLocationList": isLocationInLocationList,
"isLocationAllowed": isLocationAllowed,
"buildLogFormatUpstream": buildLogFormatUpstream,
"buildDenyVariable": buildDenyVariable,
"getenv": os.Getenv,
"contains": strings.Contains,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"trimSpace": strings.TrimSpace,
"toUpper": strings.ToUpper,
"toLower": strings.ToLower,
"formatIP": formatIP,
"buildNextUpstream": buildNextUpstream,
"getIngressInformation": getIngressInformation,
"escapeLiteralDollar": escapeLiteralDollar,
"shouldConfigureLuaRestyWAF": shouldConfigureLuaRestyWAF,
"buildLuaSharedDictionaries": buildLuaSharedDictionaries,
"luaConfigurationRequestBodySize": luaConfigurationRequestBodySize,
"buildLocation": buildLocation,
"buildAuthLocation": buildAuthLocation,
"shouldApplyGlobalAuth": shouldApplyGlobalAuth,
"buildAuthResponseHeaders": buildAuthResponseHeaders,
"buildProxyPass": buildProxyPass,
"filterRateLimits": filterRateLimits,
"buildRateLimitZones": buildRateLimitZones,
"buildRateLimit": buildRateLimit,
"configForLua": configForLua,
"locationConfigForLua": locationConfigForLua,
"buildResolvers": buildResolvers,
"buildUpstreamName": buildUpstreamName,
"isLocationInLocationList": isLocationInLocationList,
"isLocationAllowed": isLocationAllowed,
"buildLogFormatUpstream": buildLogFormatUpstream,
"buildDenyVariable": buildDenyVariable,
"getenv": os.Getenv,
"contains": strings.Contains,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"trimSpace": strings.TrimSpace,
"toUpper": strings.ToUpper,
"toLower": strings.ToLower,
"formatIP": formatIP,
"quote": quote,
"buildNextUpstream": buildNextUpstream,
"getIngressInformation": getIngressInformation,
"serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} {
return struct{ First, Second interface{} }{all, server}
},
@ -177,6 +179,8 @@ var (
"opentracingPropagateContext": opentracingPropagateContext,
"buildCustomErrorLocationsPerServer": buildCustomErrorLocationsPerServer,
"shouldLoadModSecurityModule": shouldLoadModSecurityModule,
"buildHTTPListener": buildHTTPListener,
"buildHTTPSListener": buildHTTPSListener,
}
)
@ -208,6 +212,21 @@ func formatIP(input string) string {
return fmt.Sprintf("[%s]", input)
}
func quote(input interface{}) string {
var inputStr string
switch input := input.(type) {
case string:
inputStr = input
break
case fmt.Stringer:
inputStr = input.String()
break
default:
inputStr = fmt.Sprintf("%v", input)
}
return fmt.Sprintf("%q", inputStr)
}
func shouldConfigureLuaRestyWAF(disableLuaRestyWAF bool, mode string) bool {
if !disableLuaRestyWAF && len(mode) > 0 {
return true
@ -216,19 +235,26 @@ func shouldConfigureLuaRestyWAF(disableLuaRestyWAF bool, mode string) bool {
return false
}
func buildLuaSharedDictionaries(s interface{}, disableLuaRestyWAF bool) string {
func buildLuaSharedDictionaries(c interface{}, s interface{}, disableLuaRestyWAF bool) string {
var out []string
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
return ""
}
servers, ok := s.([]*ingress.Server)
if !ok {
klog.Errorf("expected an '[]*ingress.Server' type but %T was returned", s)
return ""
}
out := []string{
"lua_shared_dict configuration_data 15M",
"lua_shared_dict certificate_data 16M",
for name, size := range cfg.LuaSharedDicts {
out = append(out, fmt.Sprintf("lua_shared_dict %s %dM", name, size))
}
if !disableLuaRestyWAF {
// TODO: there must be a better place for this
if _, ok := cfg.LuaSharedDicts["waf_storage"]; !ok && !disableLuaRestyWAF {
luaRestyWAFEnabled := func() bool {
for _, server := range servers {
for _, location := range server.Locations {
@ -244,38 +270,23 @@ func buildLuaSharedDictionaries(s interface{}, disableLuaRestyWAF bool) string {
}
}
return strings.Join(out, ";\n\r") + ";"
return strings.Join(out, ";\n") + ";\n"
}
func buildResolversForLua(res interface{}, disableIpv6 interface{}) string {
nss, ok := res.([]net.IP)
func luaConfigurationRequestBodySize(c interface{}) string {
cfg, ok := c.(config.Configuration)
if !ok {
klog.Errorf("expected a '[]net.IP' type but %T was returned", res)
return ""
}
no6, ok := disableIpv6.(bool)
if !ok {
klog.Errorf("expected a 'bool' type but %T was returned", disableIpv6)
return ""
klog.Errorf("expected a 'config.Configuration' type but %T was returned", c)
return "100" // just a default number
}
if len(nss) == 0 {
return ""
size := cfg.LuaSharedDicts["configuration_data"]
if size < cfg.LuaSharedDicts["certificate_data"] {
size = cfg.LuaSharedDicts["certificate_data"]
}
size = size + 1
r := []string{}
for _, ns := range nss {
if ing_net.IsIPV6(ns) {
if no6 {
continue
}
r = append(r, fmt.Sprintf("\"[%v]\"", ns))
} else {
r = append(r, fmt.Sprintf("\"%v\"", ns))
}
}
return strings.Join(r, ", ")
return fmt.Sprintf("%d", size)
}
// configForLua returns some general configuration as Lua table represented as string
@ -314,7 +325,7 @@ func locationConfigForLua(l interface{}, s interface{}, a interface{}) string {
return "{}"
}
forceSSLRedirect := location.Rewrite.ForceSSLRedirect || (len(server.SSLCert.PemFileName) > 0 && location.Rewrite.SSLRedirect)
forceSSLRedirect := location.Rewrite.ForceSSLRedirect || (server.SSLCert != nil && location.Rewrite.SSLRedirect)
forceSSLRedirect = forceSSLRedirect && !isLocationInLocationList(l, all.Cfg.NoTLSRedirectLocations)
return fmt.Sprintf(`{
@ -497,6 +508,9 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
case "AJP":
proto = ""
proxyPass = "ajp_pass"
case "FCGI":
proto = ""
proxyPass = "fastcgi_pass"
}
upstreamName := "upstream_balancer"
@ -1088,3 +1102,169 @@ func shouldLoadModSecurityModule(c interface{}, s interface{}) bool {
// Not enabled globally nor via annotation on a location, no need to load the module.
return false
}
func buildHTTPListener(t interface{}, s interface{}) string {
var out []string
tc, ok := t.(config.TemplateConfig)
if !ok {
klog.Errorf("expected a 'config.TemplateConfig' type but %T was returned", t)
return ""
}
hostname, ok := s.(string)
if !ok {
klog.Errorf("expected a 'string' type but %T was returned", s)
return ""
}
addrV4 := []string{""}
if len(tc.Cfg.BindAddressIpv4) > 0 {
addrV4 = tc.Cfg.BindAddressIpv4
}
co := commonListenOptions(tc, hostname)
out = append(out, httpListener(addrV4, co, tc)...)
if !tc.IsIPV6Enabled {
return strings.Join(out, "\n")
}
addrV6 := []string{"[::]"}
if len(tc.Cfg.BindAddressIpv6) > 0 {
addrV6 = tc.Cfg.BindAddressIpv6
}
out = append(out, httpListener(addrV6, co, tc)...)
return strings.Join(out, "\n")
}
func buildHTTPSListener(t interface{}, s interface{}) string {
var out []string
tc, ok := t.(config.TemplateConfig)
if !ok {
klog.Errorf("expected a 'config.TemplateConfig' type but %T was returned", t)
return ""
}
hostname, ok := s.(string)
if !ok {
klog.Errorf("expected a 'string' type but %T was returned", s)
return ""
}
/*
if server.SSLCert == nil && server.Hostname != "_" {
return ""
}
*/
co := commonListenOptions(tc, hostname)
addrV4 := []string{""}
if len(tc.Cfg.BindAddressIpv4) > 0 {
addrV4 = tc.Cfg.BindAddressIpv4
}
out = append(out, httpsListener(addrV4, co, tc)...)
if !tc.IsIPV6Enabled {
return strings.Join(out, "\n")
}
addrV6 := []string{"[::]"}
if len(tc.Cfg.BindAddressIpv6) > 0 {
addrV6 = tc.Cfg.BindAddressIpv6
}
out = append(out, httpsListener(addrV6, co, tc)...)
return strings.Join(out, "\n")
}
func commonListenOptions(template config.TemplateConfig, hostname string) string {
var out []string
if template.Cfg.UseProxyProtocol {
out = append(out, "proxy_protocol")
}
if hostname != "_" {
return strings.Join(out, " ")
}
// setup options that are valid only once per port
out = append(out, "default_server")
if template.Cfg.ReusePort {
out = append(out, "reuseport")
}
out = append(out, fmt.Sprintf("backlog=%v", template.BacklogSize))
return strings.Join(out, " ")
}
func httpListener(addresses []string, co string, tc config.TemplateConfig) []string {
out := make([]string, 0)
for _, address := range addresses {
l := make([]string, 0)
l = append(l, "listen")
if address == "" {
l = append(l, fmt.Sprintf("%v", tc.ListenPorts.HTTP))
} else {
l = append(l, fmt.Sprintf("%v:%v", address, tc.ListenPorts.HTTP))
}
l = append(l, co)
l = append(l, ";")
out = append(out, strings.Join(l, " "))
}
return out
}
func httpsListener(addresses []string, co string, tc config.TemplateConfig) []string {
out := make([]string, 0)
for _, address := range addresses {
l := make([]string, 0)
l = append(l, "listen")
if tc.IsSSLPassthroughEnabled {
if address == "" {
l = append(l, fmt.Sprintf("%v", tc.ListenPorts.SSLProxy))
} else {
l = append(l, fmt.Sprintf("%v:%v", address, tc.ListenPorts.SSLProxy))
}
l = append(l, "proxy_protocol")
} else {
if address == "" {
l = append(l, fmt.Sprintf("%v", tc.ListenPorts.HTTPS))
} else {
l = append(l, fmt.Sprintf("%v:%v", address, tc.ListenPorts.HTTPS))
}
if tc.Cfg.UseProxyProtocol {
l = append(l, "proxy_protocol")
}
}
l = append(l, co)
l = append(l, "ssl")
if tc.Cfg.UseHTTP2 {
l = append(l, "http2")
}
l = append(l, ";")
out = append(out, strings.Join(l, " "))
}
return out
}

View file

@ -17,20 +17,20 @@ limitations under the License.
package template
import (
"encoding/base64"
"fmt"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"reflect"
"strings"
"testing"
"encoding/base64"
"fmt"
jsoniter "github.com/json-iterator/go"
networking "k8s.io/api/networking/v1beta1"
"k8s.io/ingress-nginx/internal/file"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
@ -39,8 +39,18 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
"k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/nginx"
)
func init() {
// the default value of nginx.TemplatePath assumes the template exists in
// the root filesystem and not in the rootfs directory
path, err := filepath.Abs(filepath.Join("../../../../rootfs/", nginx.TemplatePath))
if err == nil {
nginx.TemplatePath = path
}
}
var (
// TODO: add tests for SSLPassthrough
tmplFuncTestcases = map[string]struct {
@ -168,7 +178,14 @@ proxy_pass http://upstream_balancer;`,
func TestBuildLuaSharedDictionaries(t *testing.T) {
invalidType := &ingress.Ingress{}
expected := ""
actual := buildLuaSharedDictionaries(invalidType, true)
// config lua dict
cfg := config.Configuration{
LuaSharedDicts: map[string]int{
"configuration_data": 10, "certificate_data": 20,
},
}
actual := buildLuaSharedDictionaries(cfg, invalidType, true)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
@ -184,20 +201,41 @@ func TestBuildLuaSharedDictionaries(t *testing.T) {
Locations: []*ingress.Location{{Path: "/", LuaRestyWAF: luarestywaf.Config{}}},
},
}
configuration := buildLuaSharedDictionaries(servers, false)
if !strings.Contains(configuration, "lua_shared_dict configuration_data") {
// returns value from config
configuration := buildLuaSharedDictionaries(cfg, servers, false)
if !strings.Contains(configuration, "lua_shared_dict configuration_data 10M;\n") {
t.Errorf("expected to include 'configuration_data' but got %s", configuration)
}
if !strings.Contains(configuration, "lua_shared_dict certificate_data 20M;\n") {
t.Errorf("expected to include 'certificate_data' but got %s", configuration)
}
if strings.Contains(configuration, "waf_storage") {
t.Errorf("expected to not include 'waf_storage' but got %s", configuration)
}
servers[1].Locations[0].LuaRestyWAF = luarestywaf.Config{Mode: "ACTIVE"}
configuration = buildLuaSharedDictionaries(servers, false)
configuration = buildLuaSharedDictionaries(cfg, servers, false)
if !strings.Contains(configuration, "lua_shared_dict waf_storage") {
t.Errorf("expected to configure 'waf_storage', but got %s", configuration)
}
// test invalid config
configuration = buildLuaSharedDictionaries(invalidType, servers, false)
if expected != actual {
t.Errorf("Expected '%v' but returned '%v' ", expected, actual)
}
}
func TestLuaConfigurationRequestBodySize(t *testing.T) {
cfg := config.Configuration{
LuaSharedDicts: map[string]int{
"configuration_data": 10, "certificate_data": 20,
},
}
size := luaConfigurationRequestBodySize(cfg)
if "21" != size {
t.Errorf("expected the size to be 20 but got: %v", size)
}
}
func TestFormatIP(t *testing.T) {
@ -219,6 +257,21 @@ func TestFormatIP(t *testing.T) {
}
}
func TestQuote(t *testing.T) {
cases := map[interface{}]string{
"foo": `"foo"`,
"\"foo\"": `"\"foo\""`,
"foo\nbar": `"foo\nbar"`,
10: `"10"`,
}
for input, output := range cases {
actual := quote(input)
if actual != output {
t.Errorf("quote('%s'): expected '%v' but returned '%v'", input, output, actual)
}
}
}
func TestBuildLocation(t *testing.T) {
invalidType := &ingress.Ingress{}
expected := "/"
@ -408,16 +461,13 @@ func TestTemplateWithData(t *testing.T) {
dat.ListenPorts = &config.ListenPorts{}
}
fs, err := file.NewFakeFS()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
ngxTpl, err := NewTemplate("/etc/nginx/template/nginx.tmpl", fs)
ngxTpl, err := NewTemplate(nginx.TemplatePath)
if err != nil {
t.Errorf("invalid NGINX template: %v", err)
}
dat.Cfg.DefaultSSLCertificate = &ingress.SSLCert{}
rt, err := ngxTpl.Write(dat)
if err != nil {
t.Errorf("invalid NGINX template: %v", err)
@ -452,12 +502,7 @@ func BenchmarkTemplateWithData(b *testing.B) {
b.Errorf("unexpected error unmarshalling json: %v", err)
}
fs, err := file.NewFakeFS()
if err != nil {
b.Fatalf("unexpected error: %v", err)
}
ngxTpl, err := NewTemplate("/etc/nginx/template/nginx.tmpl", fs)
ngxTpl, err := NewTemplate(nginx.TemplatePath)
if err != nil {
b.Errorf("invalid NGINX template: %v", err)
}
@ -550,43 +595,6 @@ func TestBuildForwardedFor(t *testing.T) {
}
}
func TestBuildResolversForLua(t *testing.T) {
ipOne := net.ParseIP("192.0.0.1")
ipTwo := net.ParseIP("2001:db8:1234:0000:0000:0000:0000:0000")
ipList := []net.IP{ipOne, ipTwo}
invalidType := &ingress.Ingress{}
expected := ""
actual := buildResolversForLua(invalidType, false)
// Invalid Type for []net.IP
if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
actual = buildResolversForLua(ipList, invalidType)
// Invalid Type for bool
if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
expected = "\"192.0.0.1\", \"[2001:db8:1234::]\""
actual = buildResolversForLua(ipList, false)
if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
expected = "\"192.0.0.1\""
actual = buildResolversForLua(ipList, true)
if expected != actual {
t.Errorf("Expected '%v' but returned '%v'", expected, actual)
}
}
func TestBuildResolvers(t *testing.T) {
ipOne := net.ParseIP("192.0.0.1")
ipTwo := net.ParseIP("2001:db8:1234:0000:0000:0000:0000:0000")
@ -874,6 +882,7 @@ func TestOpentracingPropagateContext(t *testing.T) {
&ingress.Location{BackendProtocol: "GRPC"}: "opentracing_grpc_propagate_context",
&ingress.Location{BackendProtocol: "GRPCS"}: "opentracing_grpc_propagate_context",
&ingress.Location{BackendProtocol: "AJP"}: "opentracing_propagate_context",
&ingress.Location{BackendProtocol: "FCGI"}: "opentracing_propagate_context",
"not a location": "opentracing_propagate_context",
}