Skip to content

Commit

Permalink
allow specifying custom dh param
Browse files Browse the repository at this point in the history
fixes #162
  • Loading branch information
glerchundi committed Mar 8, 2017
1 parent fedf342 commit e1c1dfa
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 18 deletions.
2 changes: 1 addition & 1 deletion controllers/nginx/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ The recommendation above prioritizes algorithms that provide perfect [forward se
Please check the [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/).


**ssl-dh-param:** sets the Base64 string that contains Diffie-Hellman key to help with "Perfect Forward Secrecy".
**ssl-dh-param:** Sets the name of the secret that contains Diffie-Hellman key to help with "Perfect Forward Secrecy".
https://www.openssl.org/docs/manmaster/apps/dhparam.html
https://wiki.mozilla.org/Security/Server_Side_TLS#DHE_handshake_and_dhparam
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
Expand Down
28 changes: 28 additions & 0 deletions controllers/nginx/pkg/cmd/controller/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/http"
"os"
"os/exec"
"strings"
"syscall"
"time"

Expand All @@ -38,6 +39,7 @@ import (
"k8s.io/ingress/controllers/nginx/pkg/version"
"k8s.io/ingress/core/pkg/ingress"
"k8s.io/ingress/core/pkg/ingress/defaults"
"k8s.io/ingress/core/pkg/net/ssl"
)

const (
Expand Down Expand Up @@ -346,6 +348,32 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) ([]byte, er
}
}

sslDHParam := ""
if cfg.SSLDHParam != "" {
secretName := cfg.SSLDHParam
s, exists, err := n.storeLister.Secret.GetByKey(secretName)
if err != nil {
glog.Warningf("unexpected error reading secret %v: %v", secretName, err)
}

if exists {
secret := s.(*api.Secret)
nsSecName := strings.Replace(secretName, "/", "-", -1)

dh, ok := secret.Data["dhparam.pem"]
if ok {
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh)
if err != nil {
glog.Warningf("unexpected error adding or updating dhparam %v file: %v", nsSecName, err)
} else {
sslDHParam = pemFileName
}
}
}
}

cfg.SSLDHParam = sslDHParam

content, err := n.t.Write(config.TemplateConfig{
ProxySetHeaders: setHeaders,
MaxOpenFiles: maxOpenFiles,
Expand Down
2 changes: 1 addition & 1 deletion controllers/nginx/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ type Configuration struct {
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers
SSLCiphers string `json:"ssl-ciphers,omitempty"`

// Base64 string that contains Diffie-Hellman key to help with "Perfect Forward Secrecy"
// The secret that contains Diffie-Hellman key to help with "Perfect Forward Secrecy"
// https://www.openssl.org/docs/manmaster/apps/dhparam.html
// https://wiki.mozilla.org/Security/Server_Side_TLS#DHE_handshake_and_dhparam
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
Expand Down
59 changes: 43 additions & 16 deletions core/pkg/net/ssl/ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,25 +175,52 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) {
}, nil
}

// SearchDHParamFile iterates all the secrets mounted inside the /etc/nginx-ssl directory
// in order to find a file with the name dhparam.pem. If such file exists it will
// returns the path. If not it just returns an empty string
func SearchDHParamFile(baseDir string) string {
files, _ := ioutil.ReadDir(baseDir)
for _, file := range files {
if !file.IsDir() {
continue
}
// AddOrUpdateDHParam creates a dh parameters file with the specified name
func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
pemName := fmt.Sprintf("%v.pem", name)
pemFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, pemName)

dhPath := fmt.Sprintf("%v/%v/dhparam.pem", baseDir, file.Name())
if _, err := os.Stat(dhPath); err == nil {
glog.Infof("using file '%v' for parameter ssl_dhparam", dhPath)
return dhPath
}
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)

glog.V(3).Infof("Creating temp file %v for DH param: %v", tempPemFile.Name(), pemName)
if err != nil {
return "", fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err)
}

_, err = tempPemFile.Write(dh)
if err != nil {
return "", fmt.Errorf("could not write to pem file %v: %v", tempPemFile.Name(), err)
}

err = tempPemFile.Close()
if err != nil {
return "", fmt.Errorf("could not close temp pem file %v: %v", tempPemFile.Name(), err)
}

pemCerts, err := ioutil.ReadFile(tempPemFile.Name())
if err != nil {
_ = os.Remove(tempPemFile.Name())
return "", err
}

pemBlock, _ := pem.Decode(pemCerts)
if pemBlock == nil {
_ = os.Remove(tempPemFile.Name())
return "", fmt.Errorf("No valid PEM formatted block found")
}

// If the file does not start with 'BEGIN DH PARAMETERS' it's invalid and must not be used.
if pemBlock.Type != "DH PARAMETERS" {
_ = os.Remove(tempPemFile.Name())
return "", fmt.Errorf("Certificate %v contains invalid data", name)
}

err = os.Rename(tempPemFile.Name(), pemFileName)
if err != nil {
return "", fmt.Errorf("could not move temp pem file %v to destination %v: %v", tempPemFile.Name(), pemFileName, err)
}

glog.Warning("no file dhparam.pem found in secrets")
return ""
return pemFileName, nil
}

// PemSHA1 returns the SHA1 of a pem file. This is used to
Expand Down
79 changes: 79 additions & 0 deletions examples/customization/ssl-dh-param/nginx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Deploying the Nginx Ingress controller

This example aims to demonstrate the deployment of an nginx ingress controller and
use a ConfigMap to configure custom Diffie-Hellman parameters file to help with
"Perfect Forward Secrecy".

## Default Backend

The default backend is a Service capable of handling all url paths and hosts the
nginx controller doesn't understand. This most basic implementation just returns
a 404 page:

```console
$ kubectl apply -f default-backend.yaml
deployment "default-http-backend" created
service "default-http-backend" created

$ kubectl -n kube-system get po
NAME READY STATUS RESTARTS AGE
default-http-backend-2657704409-qgwdd 1/1 Running 0 28s
```

## Custom configuration

```console
$ cat nginx-load-balancer-conf.yaml
apiVersion: v1
data:
ssl-dh-param: "kube-system/lb-dhparam"
kind: ConfigMap
metadata:
name: nginx-load-balancer-conf
```

```console
$ kubectl create -f nginx-load-balancer-conf.yaml
```

## Custom DH parameters secret

```console
$> openssl dhparam 1024 2> /dev/null | base64
LS0tLS1CRUdJTiBESCBQQVJBTUVURVJ...
```

```console
$ cat ssl-dh-param.yaml
apiVersion: v1
data:
dhparam.pem: "LS0tLS1CRUdJTiBESCBQQVJBTUVURVJ..."
kind: Secret
type: Opaque
metadata:
name: lb-dhparam
namespace: kube-system
```

```console
$ kubectl create -f ssl-dh-param.yaml
```

## Controller

You can deploy the controller as follows:

```console
$ kubectl apply -f nginx-ingress-controller.yaml
deployment "nginx-ingress-controller" created

$ kubectl -n kube-system get po
NAME READY STATUS RESTARTS AGE
default-http-backend-2657704409-qgwdd 1/1 Running 0 2m
nginx-ingress-controller-873061567-4n3k2 1/1 Running 0 42s
```

## Test

Check the contents of the configmap is present in the nginx.conf file using:
`kubectl exec nginx-ingress-controller-873061567-4n3k2 -n kube-system cat /etc/nginx/nginx.conf`
51 changes: 51 additions & 0 deletions examples/customization/ssl-dh-param/nginx/default-backend.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: default-http-backend
labels:
k8s-app: default-http-backend
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: default-http-backend
spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
# Any image is permissable as long as:
# 1. It serves a 404 page at /
# 2. It serves 200 on a /healthz endpoint
image: gcr.io/google_containers/defaultbackend:1.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
namespace: kube-system
labels:
k8s-app: default-http-backend
spec:
ports:
- port: 80
targetPort: 8080
selector:
k8s-app: default-http-backend
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
labels:
k8s-app: nginx-ingress-controller
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: nginx-ingress-controller
spec:
# hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
# however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host
# that said, since hostPort is broken on CNI (https://github.com/kubernetes/kubernetes/issues/31307) we have to use hostNetwork where CNI is used
# like with kubeadm
# hostNetwork: true
terminationGracePeriodSeconds: 60
containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
name: nginx-ingress-controller
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 1
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- /nginx-ingress-controller
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
- --configmap=$(POD_NAMESPACE)/nginx-load-balancer-conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
data:
ssl-dh-param: "kube-system/lb-dhparam"
kind: ConfigMap
metadata:
name: nginx-load-balancer-conf
namespace: kube-system
8 changes: 8 additions & 0 deletions examples/customization/ssl-dh-param/nginx/ssl-dh-param.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
data:
dhparam.pem: "...base64 encoded data..."
kind: Secret
type: Opaque
metadata:
name: lb-dhparam
namespace: kube-system

0 comments on commit e1c1dfa

Please sign in to comment.