Skip to content

Commit dc30bc8

Browse files
authored
Merge pull request #106 from nginxinc/plus-controller
Add new features to Plus controller
2 parents 9895433 + 853f312 commit dc30bc8

File tree

9 files changed

+234
-15
lines changed

9 files changed

+234
-15
lines changed

nginx-plus-controller/controller/controller.go

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,14 @@ func (lbc *LoadBalancerController) syncEndp(key string) {
335335
if !isNginxIngress(&ing) {
336336
continue
337337
}
338-
ingEx := lbc.createIngress(&ing)
338+
ingEx, err := lbc.createIngress(&ing)
339+
if err != nil {
340+
glog.Warningf("Error updating endpoints for %v/%v: %v, skipping", ing.Namespace, ing.Name, err)
341+
continue
342+
}
339343
glog.V(3).Infof("Updating Endpoints for %v/%v", ing.Name, ing.Namespace)
340344
name := ing.Namespace + "-" + ing.Name
341-
lbc.cnf.UpdateEndpoints(name, &ingEx)
345+
lbc.cnf.UpdateEndpoints(name, ingEx)
342346
}
343347
}
344348

@@ -363,6 +367,20 @@ func (lbc *LoadBalancerController) syncCfgm(key string) {
363367
if proxyReadTimeout, exists := cfgm.Data["proxy-read-timeout"]; exists {
364368
cfg.ProxyReadTimeout = proxyReadTimeout
365369
}
370+
if proxyHideHeaders, exists, err := nginx.GetMapKeyAsStringSlice(cfgm.Data, "proxy-hide-headers", cfgm); exists {
371+
if err != nil {
372+
glog.Error(err)
373+
} else {
374+
cfg.ProxyHideHeaders = proxyHideHeaders
375+
}
376+
}
377+
if proxyPassHeaders, exists, err := nginx.GetMapKeyAsStringSlice(cfgm.Data, "proxy-pass-headers", cfgm); exists {
378+
if err != nil {
379+
glog.Error(err)
380+
} else {
381+
cfg.ProxyPassHeaders = proxyPassHeaders
382+
}
383+
}
366384
if clientMaxBodySize, exists := cfgm.Data["client-max-body-size"]; exists {
367385
cfg.ClientMaxBodySize = clientMaxBodySize
368386
}
@@ -412,6 +430,57 @@ func (lbc *LoadBalancerController) syncCfgm(key string) {
412430
}
413431
}
414432

433+
if proxyProtocol, exists, err := nginx.GetMapKeyAsBool(cfgm.Data, "proxy-protocol", cfgm); exists {
434+
if err != nil {
435+
glog.Error(err)
436+
} else {
437+
cfg.ProxyProtocol = proxyProtocol
438+
}
439+
}
440+
441+
// ngx_http_realip_module
442+
if realIPHeader, exists := cfgm.Data["real-ip-header"]; exists {
443+
cfg.RealIPHeader = realIPHeader
444+
}
445+
if setRealIPFrom, exists, err := nginx.GetMapKeyAsStringSlice(cfgm.Data, "set-real-ip-from", cfgm); exists {
446+
if err != nil {
447+
glog.Error(err)
448+
} else {
449+
cfg.SetRealIPFrom = setRealIPFrom
450+
}
451+
}
452+
if realIPRecursive, exists, err := nginx.GetMapKeyAsBool(cfgm.Data, "real-ip-recursive", cfgm); exists {
453+
if err != nil {
454+
glog.Error(err)
455+
} else {
456+
cfg.RealIPRecursive = realIPRecursive
457+
}
458+
}
459+
460+
// SSL block
461+
if sslProtocols, exists := cfgm.Data["ssl-protocols"]; exists {
462+
cfg.MainServerSSLProtocols = sslProtocols
463+
}
464+
if sslPreferServerCiphers, exists, err := nginx.GetMapKeyAsBool(cfgm.Data, "ssl-prefer-server-ciphers", cfgm); exists {
465+
if err != nil {
466+
glog.Error(err)
467+
} else {
468+
cfg.MainServerSSLPreferServerCiphers = sslPreferServerCiphers
469+
}
470+
}
471+
if sslCiphers, exists := cfgm.Data["ssl-ciphers"]; exists {
472+
cfg.MainServerSSLCiphers = strings.Trim(sslCiphers, "\n")
473+
}
474+
if sslDHParamFile, exists := cfgm.Data["ssl-dhparam-file"]; exists {
475+
sslDHParamFile = strings.Trim(sslDHParamFile, "\n")
476+
fileName, err := lbc.cnf.AddOrUpdateDHParam(sslDHParamFile)
477+
if err != nil {
478+
glog.Errorf("Configmap %s/%s: Could not update dhparams: %v", cfgm.GetNamespace(), cfgm.GetName(), err)
479+
} else {
480+
cfg.MainServerSSLDHParam = fileName
481+
}
482+
}
483+
415484
if logFormat, exists := cfgm.Data["log-format"]; exists {
416485
cfg.MainLogFormat = logFormat
417486
}
@@ -462,8 +531,12 @@ func (lbc *LoadBalancerController) syncIng(key string) {
462531
glog.V(2).Infof("Adding or Updating Ingress: %v\n", key)
463532

464533
ing := obj.(*extensions.Ingress)
465-
ingEx := lbc.createIngress(ing)
466-
lbc.cnf.AddOrUpdateIngress(name, &ingEx)
534+
ingEx, err := lbc.createIngress(ing)
535+
if err != nil {
536+
lbc.ingQueue.requeueAfter(key, err, 5*time.Second)
537+
return
538+
}
539+
lbc.cnf.AddOrUpdateIngress(name, ingEx)
467540
}
468541
}
469542

@@ -501,8 +574,8 @@ func (lbc *LoadBalancerController) getIngressForEndpoints(obj interface{}) []ext
501574
return ings
502575
}
503576

504-
func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) nginx.IngressEx {
505-
ingEx := nginx.IngressEx{
577+
func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) (*nginx.IngressEx, error) {
578+
ingEx := &nginx.IngressEx{
506579
Ingress: ing,
507580
}
508581

@@ -511,8 +584,7 @@ func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) nginx.
511584
secretName := tls.SecretName
512585
secret, err := lbc.client.Secrets(ing.Namespace).Get(secretName)
513586
if err != nil {
514-
glog.Warningf("Error retrieving secret %v for Ingress %v: %v", secretName, ing.Name, err)
515-
continue
587+
return nil, fmt.Errorf("Error retrieving secret %v for Ingress %v: %v", secretName, ing.Name, err)
516588
}
517589
ingEx.Secrets[secretName] = secret
518590
}
@@ -542,7 +614,7 @@ func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) nginx.
542614
}
543615
}
544616

545-
return ingEx
617+
return ingEx, nil
546618
}
547619

548620
func (lbc *LoadBalancerController) getEndpointsForIngressBackend(backend *extensions.IngressBackend, namespace string) ([]string, error) {

nginx-plus-controller/controller/utils.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ func (t *taskQueue) requeue(key string, err error) {
5959
t.queue.Add(key)
6060
}
6161

62+
func (t *taskQueue) requeueAfter(key string, err error, after time.Duration) {
63+
glog.Errorf("Requeuing %v after %s, err %v", key, after.String(), err)
64+
go func(key string, after time.Duration) {
65+
time.Sleep(after)
66+
t.queue.Add(key)
67+
}(key, after)
68+
}
69+
6270
// worker processes work in the queue through sync.
6371
func (t *taskQueue) worker() {
6472
for {

nginx-plus-controller/nginx/config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,23 @@ type Config struct {
1313
ProxyBuffers string
1414
ProxyBufferSize string
1515
ProxyMaxTempFileSize string
16+
ProxyProtocol bool
17+
ProxyHideHeaders []string
18+
ProxyPassHeaders []string
1619
HSTS bool
1720
HSTSMaxAge int64
1821
HSTSIncludeSubdomains bool
22+
23+
// http://nginx.org/en/docs/http/ngx_http_realip_module.html
24+
RealIPHeader string
25+
SetRealIPFrom []string
26+
RealIPRecursive bool
27+
28+
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html
29+
MainServerSSLProtocols string
30+
MainServerSSLPreferServerCiphers bool
31+
MainServerSSLCiphers string
32+
MainServerSSLDHParam string
1933
}
2034

2135
// NewDefaultConfig creates a Config with default values

nginx-plus-controller/nginx/configurator.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ func NewConfigurator(nginx *NginxController, config *Config, nginxAPI *NginxAPIC
3232
return &cnf
3333
}
3434

35+
func (cnf *Configurator) AddOrUpdateDHParam(content string) (string, error) {
36+
return cnf.nginx.AddOrUpdateDHParam(content)
37+
}
38+
3539
// AddOrUpdateIngress adds or updates NGINX configuration for an Ingress resource
3640
func (cnf *Configurator) AddOrUpdateIngress(name string, ingEx *IngressEx) {
3741
cnf.lock.Lock()
@@ -115,10 +119,16 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri
115119
server := Server{
116120
Name: serverName,
117121
HTTP2: ingCfg.HTTP2,
122+
ProxyProtocol: ingCfg.ProxyProtocol,
118123
HSTS: ingCfg.HSTS,
119124
HSTSMaxAge: ingCfg.HSTSMaxAge,
120125
HSTSIncludeSubdomains: ingCfg.HSTSIncludeSubdomains,
121126
StatusZone: statuzZone,
127+
RealIPHeader: ingCfg.RealIPHeader,
128+
SetRealIPFrom: ingCfg.SetRealIPFrom,
129+
RealIPRecursive: ingCfg.RealIPRecursive,
130+
ProxyHideHeaders: ingCfg.ProxyHideHeaders,
131+
ProxyPassHeaders: ingCfg.ProxyPassHeaders,
122132
}
123133

124134
if pemFile, ok := pems[serverName]; ok {
@@ -164,10 +174,16 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri
164174
server := Server{
165175
Name: serverName,
166176
HTTP2: ingCfg.HTTP2,
177+
ProxyProtocol: ingCfg.ProxyProtocol,
167178
HSTS: ingCfg.HSTS,
168179
HSTSMaxAge: ingCfg.HSTSMaxAge,
169180
HSTSIncludeSubdomains: ingCfg.HSTSIncludeSubdomains,
170181
StatusZone: statuzZone,
182+
RealIPHeader: ingCfg.RealIPHeader,
183+
SetRealIPFrom: ingCfg.SetRealIPFrom,
184+
RealIPRecursive: ingCfg.RealIPRecursive,
185+
ProxyHideHeaders: ingCfg.ProxyHideHeaders,
186+
ProxyPassHeaders: ingCfg.ProxyPassHeaders,
171187
}
172188

173189
if pemFile, ok := pems[emptyHost]; ok {
@@ -198,6 +214,20 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config {
198214
if proxyReadTimeout, exists := ingEx.Ingress.Annotations["nginx.org/proxy-read-timeout"]; exists {
199215
ingCfg.ProxyReadTimeout = proxyReadTimeout
200216
}
217+
if proxyHideHeaders, exists, err := GetMapKeyAsStringSlice(ingEx.Ingress.Annotations, "nginx.org/proxy-hide-headers", ingEx.Ingress); exists {
218+
if err != nil {
219+
glog.Error(err)
220+
} else {
221+
ingCfg.ProxyHideHeaders = proxyHideHeaders
222+
}
223+
}
224+
if proxyPassHeaders, exists, err := GetMapKeyAsStringSlice(ingEx.Ingress.Annotations, "nginx.org/proxy-pass-headers", ingEx.Ingress); exists {
225+
if err != nil {
226+
glog.Error(err)
227+
} else {
228+
ingCfg.ProxyPassHeaders = proxyPassHeaders
229+
}
230+
}
201231
if clientMaxBodySize, exists := ingEx.Ingress.Annotations["nginx.org/client-max-body-size"]; exists {
202232
ingCfg.ClientMaxBodySize = clientMaxBodySize
203233
}
@@ -451,6 +481,10 @@ func (cnf *Configurator) UpdateConfig(config *Config) {
451481
ServerNamesHashBucketSize: config.MainServerNamesHashBucketSize,
452482
ServerNamesHashMaxSize: config.MainServerNamesHashMaxSize,
453483
LogFormat: config.MainLogFormat,
484+
SSLProtocols: config.MainServerSSLProtocols,
485+
SSLCiphers: config.MainServerSSLCiphers,
486+
SSLDHParam: config.MainServerSSLDHParam,
487+
SSLPreferServerCiphers: config.MainServerSSLPreferServerCiphers,
454488
}
455489

456490
cnf.nginx.UpdateMainConfigFile(mainCfg)

nginx-plus-controller/nginx/convert.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package nginx
33
import (
44
"fmt"
55
"strconv"
6+
"strings"
67

78
"k8s.io/kubernetes/pkg/api/meta"
89
"k8s.io/kubernetes/pkg/runtime"
@@ -38,3 +39,12 @@ func GetMapKeyAsInt(m map[string]string, key string, context apiObject) (int64,
3839
}
3940
return 0, false, nil
4041
}
42+
43+
// GetMapKeyAsStringSlice tries to find and parse a key in the map as string slice splitting it on ','
44+
func GetMapKeyAsStringSlice(m map[string]string, key string, context apiObject) ([]string, bool, error) {
45+
if str, exists := m[key]; exists {
46+
slice := strings.Split(str, ",")
47+
return slice, exists, nil
48+
}
49+
return nil, false, nil
50+
}

nginx-plus-controller/nginx/convert_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package nginx
22

33
import (
4+
"reflect"
45
"testing"
56

67
"k8s.io/kubernetes/pkg/api"
@@ -153,3 +154,36 @@ func TestGetMapKeyAsIntErrorMessage(t *testing.T) {
153154
t.Errorf("The error message does not match expectations:\nGot: %v\nExpected: %v", err, expected)
154155
}
155156
}
157+
158+
//
159+
// GetMapKeyAsStringSlice
160+
//
161+
func TestGetMapKeyAsStringSlice(t *testing.T) {
162+
configMap := configMap
163+
configMap.Data = map[string]string{
164+
"key": "1.String,2.String,3.String",
165+
}
166+
167+
slice, exists, err := GetMapKeyAsStringSlice(configMap.Data, "key", &configMap)
168+
if err != nil {
169+
t.Errorf("Unexpected error: %v", err)
170+
}
171+
if !exists {
172+
t.Errorf("The key 'key' must exist in the configMap")
173+
}
174+
expected := []string{"1.String", "2.String", "3.String"}
175+
t.Log(expected)
176+
if !reflect.DeepEqual(expected, slice) {
177+
t.Errorf("Unexpected return value:\nGot: %#v\nExpected: %#v", slice, expected)
178+
}
179+
}
180+
181+
func TestGetMapKeyAsStringSliceNotFound(t *testing.T) {
182+
configMap := configMap
183+
configMap.Data = map[string]string{}
184+
185+
_, exists, _ := GetMapKeyAsStringSlice(configMap.Data, "key", &configMap)
186+
if exists {
187+
t.Errorf("The key 'key' must not exist in the configMap")
188+
}
189+
}

nginx-plus-controller/nginx/ingress.tmpl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,28 @@ upstream {{$upstream.Name}} {
99

1010
{{range $server := .Servers}}
1111
server {
12-
listen 80;
12+
listen 80{{if $server.ProxyProtocol}} proxy_protocol{{end}};
1313
{{if $server.SSL}}
14-
listen 443 ssl{{if $server.HTTP2}} http2{{end}};
14+
listen 443 ssl{{if $server.HTTP2}} http2{{end}}{{if $server.ProxyProtocol}} proxy_protocol{{end}};
1515
ssl_certificate {{$server.SSLCertificate}};
1616
ssl_certificate_key {{$server.SSLCertificateKey}};
1717
{{end}}
18+
{{range $setRealIPFrom := $server.SetRealIPFrom}}
19+
set_real_ip_from {{$setRealIPFrom}};{{end}}
20+
{{if $server.RealIPHeader}}real_ip_header {{$server.RealIPHeader}};{{end}}
21+
{{if $server.RealIPRecursive}}real_ip_recursive on;{{end}}
1822

1923
{{if $server.Name}}
2024
server_name {{$server.Name}};
2125
{{end}}
2226

2327
status_zone {{$server.StatusZone}};
2428

29+
{{range $proxyHideHeader := $server.ProxyHideHeaders}}
30+
proxy_hide_header {{$proxyHideHeader}};{{end}}
31+
{{range $proxyPassHeader := $server.ProxyPassHeaders}}
32+
proxy_pass_header {{$proxyPassHeader}};{{end}}
33+
2534
{{if $server.SSL}}
2635
if ($scheme = http) {
2736
return 301 https://$host$request_uri;

nginx-plus-controller/nginx/nginx.conf.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ http {
3939
'' close;
4040
}
4141

42+
{{if .SSLProtocols}}ssl_protocols {{.SSLProtocols}};{{end}}
43+
{{if .SSLCiphers}}ssl_ciphers "{{.SSLCiphers}}";{{end}}
44+
{{if .SSLPreferServerCiphers}}ssl_prefer_server_ciphers on;{{end}}
45+
{{if .SSLDHParam}}ssl_dhparam {{.SSLDHParam}};{{end}}
46+
4247
{{if .HealthStatus}}
4348
server {
4449
listen 80 default_server;

0 commit comments

Comments
 (0)