Skip to content

Commit

Permalink
Add the default server and a TLS Secret for the default server
Browse files Browse the repository at this point in the history
  • Loading branch information
pleshakov committed Aug 21, 2017
1 parent 373a6b2 commit f0d6362
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ nginx-plus-ingress
# Visual Studio Code settings
.vscode

# Default certificate and key
default.pem

3 changes: 3 additions & 0 deletions nginx-controller/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ COPY nginx-ingress nginx/templates/nginx.ingress.tmpl nginx/templates/nginx.tmpl

RUN rm /etc/nginx/conf.d/*

RUN mkdir -p /etc/nginx/ssl
ADD default.pem /etc/nginx/ssl

ENTRYPOINT ["/nginx-ingress"]
3 changes: 3 additions & 0 deletions nginx-controller/DockerfileForAlpine
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ COPY nginx-ingress nginx/templates/nginx.ingress.tmpl nginx/templates/nginx.tmpl

RUN rm /etc/nginx/conf.d/*

RUN mkdir -p /etc/nginx/ssl
ADD default.pem /etc/nginx/ssl

ENTRYPOINT ["/nginx-ingress"]
3 changes: 3 additions & 0 deletions nginx-controller/DockerfileForPlus
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ RUN mkdir -p /var/lib/nginx/state && chown -R nginx:nginx /var/lib/nginx
COPY nginx-ingress nginx/templates/nginx-plus.ingress.tmpl nginx/templates/nginx-plus.tmpl /
RUN rm /etc/nginx/conf.d/*

RUN mkdir -p /etc/nginx/ssl
ADD default.pem /etc/nginx/ssl

ENTRYPOINT ["/nginx-ingress"]
5 changes: 4 additions & 1 deletion nginx-controller/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ else
go test ./...
endif

container: test nginx-ingress
certificate-and-key:
./generate_default_cert_and_key.sh

container: test nginx-ingress certificate-and-key
docker build -f $(DOCKERFILE) -t $(PREFIX):$(TAG) .

push: container
Expand Down
49 changes: 40 additions & 9 deletions nginx-controller/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,19 @@ type LoadBalancerController struct {
watchNginxConfigMaps bool
nginxPlus bool
recorder record.EventRecorder
defaultServerSecret string
}

var keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc

// NewLoadBalancerController creates a controller
func NewLoadBalancerController(kubeClient kubernetes.Interface, resyncPeriod time.Duration, namespace string, cnf *nginx.Configurator, nginxConfigMaps string, nginxPlus bool) (*LoadBalancerController, error) {
func NewLoadBalancerController(kubeClient kubernetes.Interface, resyncPeriod time.Duration, namespace string, cnf *nginx.Configurator, nginxConfigMaps string, defaultServerSecret string, nginxPlus bool) (*LoadBalancerController, error) {
lbc := LoadBalancerController{
client: kubeClient,
stopCh: make(chan struct{}),
cnf: cnf,
nginxPlus: nginxPlus,
client: kubeClient,
stopCh: make(chan struct{}),
cnf: cnf,
defaultServerSecret: defaultServerSecret,
nginxPlus: nginxPlus,
}

eventBroadcaster := record.NewBroadcaster()
Expand Down Expand Up @@ -203,6 +205,14 @@ func NewLoadBalancerController(kubeClient kubernetes.Interface, resyncPeriod tim
&api_v1.Endpoints{}, resyncPeriod, endpHandlers)

secrHandlers := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
secr := obj.(*api_v1.Secret)
nsname := secr.Namespace + "/" + secr.Name
if nsname == lbc.defaultServerSecret {
glog.V(3).Infof("Adding default server Secret: %v", secr.Name)
lbc.syncQueue.enqueue(obj)
}
},
DeleteFunc: func(obj interface{}) {
remSecr, isSecr := obj.(*api_v1.Secret)
if !isSecr {
Expand Down Expand Up @@ -244,7 +254,7 @@ func NewLoadBalancerController(kubeClient kubernetes.Interface, resyncPeriod tim
&api_v1.Secret{}, resyncPeriod, secrHandlers)

if nginxConfigMaps != "" {
nginxConfigMapsNS, nginxConfigMapsName, err := parseNamespaceName(nginxConfigMaps)
nginxConfigMapsNS, nginxConfigMapsName, err := ParseNamespaceName(nginxConfigMaps)
if err != nil {
glog.Warning(err)
} else {
Expand Down Expand Up @@ -644,7 +654,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) {
return
}

_, name, err := parseNamespaceName(key)
_, name, err := ParseNamespaceName(key)
if err != nil {
glog.Warningf("Secret key %v is invalid: %v", key, err)
return
Expand All @@ -669,11 +679,32 @@ func (lbc *LoadBalancerController) syncSecret(task Task) {
lbc.syncQueue.enqueue(&ing)
lbc.recorder.Eventf(&ing, api_v1.EventTypeWarning, "Rejected", "%v/%v was rejected due to deleted Secret %v: %v", ing.Namespace, ing.Name, key)
}

if key == lbc.defaultServerSecret {
glog.Warningf("The default server Secret %v was removed. Retaining the Secret.")
}
} else {
glog.V(2).Infof("Updating Secret: %v\n", key)

secret := obj.(*api_v1.Secret)

if key == lbc.defaultServerSecret {
err := ValidateTLSSecret(secret)
if err != nil {
glog.Errorf("Couldn't validate the default server Secret %v: %v", key, err)
lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "Rejected", "the default server Secret %v was rejected, using the previous version: %v", key, err)
} else {
err := lbc.cnf.AddOrUpdateDefaultServerTLSSecret(secret)
if err != nil {
glog.Errorf("Error when updating the default server Secret %v: %v", key, err)
lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "UpdatedWithError", "the default server Secret %v was updated, but not applied: %v", key, err)

} else {
lbc.recorder.Eventf(secret, api_v1.EventTypeNormal, "Updated", "the default server Secret %v was updated", key)
}
}
}

if len(ings) > 0 {
err := ValidateTLSSecret(secret)
if err != nil {
Expand Down Expand Up @@ -919,10 +950,10 @@ func (lbc *LoadBalancerController) getServiceForIngressBackend(backend *extensio
return nil, fmt.Errorf("service %s doesn't exists", svcKey)
}

func parseNamespaceName(value string) (ns string, name string, err error) {
func ParseNamespaceName(value string) (ns string, name string, err error) {
res := strings.Split(value, "/")
if len(res) != 2 {
return "", "", fmt.Errorf("%v must follow the format <namespace>/<name>", value)
return "", "", fmt.Errorf("%q must follow the format <namespace>/<name>", value)
}
return res[0], res[1], nil
}
Expand Down
5 changes: 5 additions & 0 deletions nginx-controller/generate_default_cert_and_key.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout default.key -out default.crt -subj "/CN=NGINXIngressController"
cat default.key default.crt > default.pem
rm default.key default.crt
27 changes: 26 additions & 1 deletion nginx-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/nginxinc/kubernetes-ingress/nginx-controller/controller"
"github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx"
"github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx/plus"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/rest"
Expand Down Expand Up @@ -43,6 +44,11 @@ var (

nginxPlus = flag.Bool("nginx-plus", false,
`Enables support for NGINX Plus.`)

defaultServerSecret = flag.String("default-server-tls-secret", "",
`Specifies a secret with a TLS certificate and key for SSL termination of
the default server. The value must follow the following format: <namespace>/<name>.
If not specified, the key and the cert from /etc/nginx/default.pem is used.`)
)

func main() {
Expand Down Expand Up @@ -84,6 +90,25 @@ func main() {
nginxIngressTemplatePath = "nginx-plus.ingress.tmpl"
}
ngxc, _ := nginx.NewNginxController("/etc/nginx/", local, *healthStatus, nginxConfTemplatePath, nginxIngressTemplatePath)

if *defaultServerSecret != "" {
ns, name, err := controller.ParseNamespaceName(*defaultServerSecret)
if err != nil {
glog.Fatalf("Error parsing the default-server-tls-secret argument: %v", err)
}
secret, err := kubeClient.CoreV1().Secrets(ns).Get(name, meta_v1.GetOptions{})
if err != nil {
glog.Fatalf("Error when getting %v: %v", *defaultServerSecret, err)
}
err = controller.ValidateTLSSecret(secret)
if err != nil {
glog.Fatalf("%v is invalid: %v", *defaultServerSecret, err)
}

bytes := nginx.GenerateCertAndKeyFileContent(secret)
ngxc.AddOrUpdatePemFile(nginx.DefaultServerPemName, bytes)
}

nginxDone := make(chan error, 1)
ngxc.Start(nginxDone)

Expand All @@ -98,7 +123,7 @@ func main() {
}
cnf := nginx.NewConfigurator(ngxc, nginxConfig, nginxAPI)

lbc, _ := controller.NewLoadBalancerController(kubeClient, 30*time.Second, *watchNamespace, cnf, *nginxConfigMaps, *nginxPlus)
lbc, _ := controller.NewLoadBalancerController(kubeClient, 30*time.Second, *watchNamespace, cnf, *nginxConfigMaps, *defaultServerSecret, *nginxPlus)
go handleTermination(lbc, ngxc, nginxDone)
lbc.Run()

Expand Down
19 changes: 17 additions & 2 deletions nginx-controller/nginx/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
)

const emptyHost = ""
const DefaultServerPemName = "default"

// Configurator transforms an Ingress resource into NGINX Configuration
type Configurator struct {
Expand Down Expand Up @@ -445,11 +446,22 @@ func (cnf *Configurator) AddOrUpdateTLSSecret(secret *api_v1.Secret, reload bool

func (cnf *Configurator) addOrUpdateTLSSecret(secret *api_v1.Secret) string {
name := objectMetaToFileName(&secret.ObjectMeta)
data := generateCertAndKeyFileContent(secret)
data := GenerateCertAndKeyFileContent(secret)
return cnf.nginx.AddOrUpdatePemFile(name, data)
}

func generateCertAndKeyFileContent(secret *api_v1.Secret) []byte {
func (cnf *Configurator) AddOrUpdateDefaultServerTLSSecret(secret *api_v1.Secret) error {
data := GenerateCertAndKeyFileContent(secret)
cnf.nginx.AddOrUpdatePemFile(DefaultServerPemName, data)

if err := cnf.nginx.Reload(); err != nil {
return fmt.Errorf("Error when reloading NGINX when updating the default server Secret: %v", err)
}
return nil
}

// GenerateCertAndKeyFileContent generates a pem file content from the secret
func GenerateCertAndKeyFileContent(secret *api_v1.Secret) []byte {
var res bytes.Buffer

res.Write(secret.Data[api_v1.TLSCertKey])
Expand Down Expand Up @@ -539,6 +551,9 @@ func (cnf *Configurator) UpdateConfig(config *Config, ingExes []*IngressEx) erro
SSLCiphers: config.MainServerSSLCiphers,
SSLDHParam: config.MainServerSSLDHParam,
SSLPreferServerCiphers: config.MainServerSSLPreferServerCiphers,
HTTP2: config.HTTP2,
ServerTokens: config.ServerTokens,
ProxyProtocol: config.ProxyProtocol,
}

cnf.nginx.UpdateMainConfigFile(mainCfg)
Expand Down
10 changes: 6 additions & 4 deletions nginx-controller/nginx/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ type NginxMainConfig struct {
SSLPreferServerCiphers bool
SSLCiphers string
SSLDHParam string
HTTP2 bool
ServerTokens string
ProxyProtocol bool
}

// NewUpstreamWithDefaultServer creates an upstream with the default server.
Expand All @@ -121,11 +124,10 @@ func NewNginxController(nginxConfPath string, local bool, healthStatus bool, ngi
nginxIngressTempatePath: nginxIngressTemplatePath,
}

if !local {
createDir(ngxc.nginxCertsPath)
cfg := &NginxMainConfig{
ServerNamesHashMaxSize: NewDefaultConfig().MainServerNamesHashMaxSize,
ServerTokens: NewDefaultConfig().ServerTokens,
}

cfg := &NginxMainConfig{ServerNamesHashMaxSize: NewDefaultConfig().MainServerNamesHashMaxSize}
ngxc.UpdateMainConfigFile(cfg)

return &ngxc, nil
Expand Down
20 changes: 16 additions & 4 deletions nginx-controller/nginx/templates/nginx-plus.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,30 @@ http {
{{if .SSLPreferServerCiphers}}ssl_prefer_server_ciphers on;{{end}}
{{if .SSLDHParam}}ssl_dhparam {{.SSLDHParam}};{{end}}

{{if .HealthStatus}}

server {
listen 80 default_server;
listen 80 default_server{{if .ProxyProtocol}} proxy_protocol{{end}};
listen 443 ssl default_server{{if .HTTP2}} http2{{end}}{{if .ProxyProtocol}} proxy_protocol{{end}};

ssl_certificate /etc/nginx/ssl/default.pem;
ssl_certificate_key /etc/nginx/ssl/default.pem;

server_name _;
server_tokens "{{.ServerTokens}}";
access_log off;

{{if .HealthStatus}}
location /nginx-health {
access_log off;
default_type text/plain;
return 200 "healthy\n";
}
{{end}}

location / {
return 404;
}
}
{{end}}


# status and on-the-fly reconfiguration APIs
server {
Expand Down
18 changes: 14 additions & 4 deletions nginx-controller/nginx/templates/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,28 @@ http {
{{if .SSLPreferServerCiphers}}ssl_prefer_server_ciphers on;{{end}}
{{if .SSLDHParam}}ssl_dhparam {{.SSLDHParam}};{{end}}

{{if .HealthStatus}}
server {
listen 80 default_server;
listen 80 default_server{{if .ProxyProtocol}} proxy_protocol{{end}};
listen 443 ssl default_server{{if .HTTP2}} http2{{end}}{{if .ProxyProtocol}} proxy_protocol{{end}};

ssl_certificate /etc/nginx/ssl/default.pem;
ssl_certificate_key /etc/nginx/ssl/default.pem;

server_name _;
server_tokens "{{.ServerTokens}}";
access_log off;

{{if .HealthStatus}}
location /nginx-health {
access_log off;
default_type text/plain;
return 200 "healthy\n";
}
{{end}}

location / {
return 404;
}
}
{{end}}

include /etc/nginx/conf.d/*.conf;
}

0 comments on commit f0d6362

Please sign in to comment.