From 9f64273b9ccadb9805b2ba1bb940bc911ff96202 Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Thu, 21 Jul 2016 11:40:47 -0400 Subject: [PATCH] Use system self signed certificate as default SSL certificate --- controllers/nginx/Dockerfile | 6 ++-- controllers/nginx/README.md | 45 ++++++++++++++++++++++++------ controllers/nginx/configuration.md | 2 +- controllers/nginx/controller.go | 23 ++++++++++----- controllers/nginx/nginx.tmpl | 15 +++------- controllers/nginx/nginx/ssl.go | 2 +- controllers/nginx/utils.go | 22 +++++++++++++++ 7 files changed, 85 insertions(+), 30 deletions(-) diff --git a/controllers/nginx/Dockerfile b/controllers/nginx/Dockerfile index 1766f04e8..ad4363945 100644 --- a/controllers/nginx/Dockerfile +++ b/controllers/nginx/Dockerfile @@ -14,10 +14,12 @@ FROM gcr.io/google_containers/nginx-slim:0.8 -RUN apt-get update && apt-get install -y \ +RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \ diffutils \ + ssl-cert \ --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && make-ssl-cert generate-default-snakeoil --force-overwrite COPY nginx-ingress-controller / COPY nginx.tmpl /etc/nginx/template/nginx.tmpl diff --git a/controllers/nginx/README.md b/controllers/nginx/README.md index 3e9c07825..c7a9cf5ff 100644 --- a/controllers/nginx/README.md +++ b/controllers/nginx/README.md @@ -138,16 +138,15 @@ Check the [example](examples/tls/README.md) ### Default SSL Certificate -NGINX provides the option [default_server](http://nginx.org/en/docs/http/server_names.html) to allow a catch-all server in case of request with a not configured server name. This configuration works without issues for HTTP traffic. -In case of HTTPS NGINX requires a certificate. For this reason the Ingress controller provides the flag `--default-ssl-certificate`. The secret behind this flag contains the default certificate to be used in the mentioned case. -If this flag is not provided NGINX will reject the request with the HTTP code 444. +NGINX provides the option serve rname [_](http://nginx.org/en/docs/http/server_names.html) as a catch-all in case of requests that do not match one of the configured server names. This configuration works without issues for HTTP traffic. In case of HTTPS NGINX requires a certificate. For this reason the Ingress controller provides the flag `--default-ssl-certificate`. The secret behind this flag contains the default certificate to be used in the mentioned case. +If this flag is not provided NGINX will use a self signed certificate. Running without the flag `--default-ssl-certificate`: ``` -$ curl -v https://10.2.78.7:443 +$ curl -v https://10.2.78.7:443 -k * Rebuilt URL to: https://10.2.78.7:443/ -* Trying 10.2.78.7... +* Trying 10.2.78.4... * Connected to 10.2.78.7 (10.2.78.7) port 443 (#0) * ALPN, offering http/1.1 * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH @@ -156,9 +155,39 @@ $ curl -v https://10.2.78.7:443 CApath: /etc/ssl/certs * TLSv1.2 (OUT), TLS header, Certificate Status (22): * TLSv1.2 (OUT), TLS handshake, Client hello (1): -* Unknown SSL protocol error in connection to 10.2.78.7:443 -* Closing connection 0 -curl: (35) Unknown SSL protocol error in connection to 10.2.78.7:443 +* TLSv1.2 (IN), TLS handshake, Server hello (2): +* TLSv1.2 (IN), TLS handshake, Certificate (11): +* TLSv1.2 (IN), TLS handshake, Server key exchange (12): +* TLSv1.2 (IN), TLS handshake, Server finished (14): +* TLSv1.2 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.2 (OUT), TLS change cipher, Client hello (1): +* TLSv1.2 (OUT), TLS handshake, Finished (20): +* TLSv1.2 (IN), TLS change cipher, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 +* ALPN, server accepted to use http/1.1 +* Server certificate: +* subject: CN=foo.bar.com +* start date: Apr 13 00:50:56 2016 GMT +* expire date: Apr 13 00:50:56 2017 GMT +* issuer: CN=foo.bar.com +* SSL certificate verify result: self signed certificate (18), continuing anyway. +> GET / HTTP/1.1 +> Host: 10.2.78.7 +> User-Agent: curl/7.47.1 +> Accept: */* +> +< HTTP/1.1 404 Not Found +< Server: nginx/1.11.1 +< Date: Thu, 21 Jul 2016 15:38:46 GMT +< Content-Type: text/html +< Transfer-Encoding: chunked +< Connection: keep-alive +< Strict-Transport-Security: max-age=15724800; includeSubDomains; preload +< +The page you're looking for could not be found. + +* Connection #0 to host 10.2.78.7 left intact ``` Specifyng `--default-ssl-certificate=default/foo-tls`: diff --git a/controllers/nginx/configuration.md b/controllers/nginx/configuration.md index bdff245dd..e8b160406 100644 --- a/controllers/nginx/configuration.md +++ b/controllers/nginx/configuration.md @@ -33,7 +33,7 @@ Please check the [custom configuration](examples/custom-configuration/README.md) #### Annotations -The following annotaitons are supported: +The following annotations are supported: |Name |type| |---------------------------|------| diff --git a/controllers/nginx/controller.go b/controllers/nginx/controller.go index 57482ae0d..ec80ea3d4 100644 --- a/controllers/nginx/controller.go +++ b/controllers/nginx/controller.go @@ -870,13 +870,22 @@ func (lbc *loadBalancerController) createServers(data []interface{}) map[string] servers := make(map[string]*nginx.Server) pems := lbc.getPemsFromIngress(data) - if lbc.defSSLCertificate != "" { - ngxCert, err := lbc.getPemCertificate(lbc.defSSLCertificate) - if err == nil { - pems["_"] = ngxCert - } else { - glog.Warningf("%v", err) - } + + var ngxCert nginx.SSLCert + var err error + + if lbc.defSSLCertificate == "" { + // use system certificated generated at image build time + cert, key := getFakeSSLCert() + ngxCert, err = lbc.nginx.AddOrUpdateCertAndKey("system-snake-oil-certificate", cert, key) + } else { + ngxCert, err = lbc.getPemCertificate(lbc.defSSLCertificate) + } + + if err == nil { + pems["_"] = ngxCert + } else { + glog.Warningf("%v", err) } for _, ingIf := range data { diff --git a/controllers/nginx/nginx.tmpl b/controllers/nginx/nginx.tmpl index 4c4393df2..fb1ca360c 100644 --- a/controllers/nginx/nginx.tmpl +++ b/controllers/nginx/nginx.tmpl @@ -180,16 +180,6 @@ http { {{ end }} {{ range $server := .servers }} - {{/* Check for default SSL backend */}} - {{ if and (eq $server.Name "_") (not $server.SSL) -}} - server { - server_name {{ $server.Name }}; - listen 443; - # return protocol error. - return 444; - } - {{ end }} - server { server_name {{ $server.Name }}; listen 80{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }}; @@ -289,8 +279,11 @@ http { } {{ end }} - # default server, including healthcheck + # default server, used for NGINX healthcheck and access to nginx stats server { + # Use the port 18080 (random value just to avoid known ports) as default port for nginx. + # Changing this value requires a change in: + # https://github.com/kubernetes/contrib/blob/master/ingress/controllers/nginx/nginx/command.go#L104 listen 18080 default_server reuseport backlog={{ .backlogSize }}; location /healthz { diff --git a/controllers/nginx/nginx/ssl.go b/controllers/nginx/nginx/ssl.go index 003b9a6f1..33234756e 100644 --- a/controllers/nginx/nginx/ssl.go +++ b/controllers/nginx/nginx/ssl.go @@ -108,7 +108,7 @@ func (nginx *Manager) commonNames(pemFileName string) ([]string, error) { cn = append(cn, cert.DNSNames...) } - glog.V(2).Infof("found %v common names: %v\n", cn, len(cn)) + glog.V(3).Infof("found %v common names: %v\n", cn, len(cn)) return cn, nil } diff --git a/controllers/nginx/utils.go b/controllers/nginx/utils.go index f317b15ba..bf696e721 100644 --- a/controllers/nginx/utils.go +++ b/controllers/nginx/utils.go @@ -18,6 +18,7 @@ package main import ( "fmt" + "io/ioutil" "os" "strings" "time" @@ -273,3 +274,24 @@ func isNGINXIngress(ing *extensions.Ingress) bool { class := ingAnnotations(ing.ObjectMeta.Annotations).ingressClass() return class == "" || class == nginxIngressClass } + +const ( + snakeOilPem = "/etc/ssl/certs/ssl-cert-snakeoil.pem" + snakeOilKey = "/etc/ssl/private/ssl-cert-snakeoil.key" +) + +// getFakeSSLCert returns the snake oil ssl certificate created by the command +// make-ssl-cert generate-default-snakeoil --force-overwrite +func getFakeSSLCert() (string, string) { + cert, err := ioutil.ReadFile(snakeOilPem) + if err != nil { + return "", "" + } + + key, err := ioutil.ReadFile(snakeOilKey) + if err != nil { + return "", "" + } + + return string(cert), string(key) +}