parent
fedf342e91
commit
e1c1dfadc7
9 changed files with 271 additions and 18 deletions
|
@ -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/).
|
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://www.openssl.org/docs/manmaster/apps/dhparam.html
|
||||||
https://wiki.mozilla.org/Security/Server_Side_TLS#DHE_handshake_and_dhparam
|
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
|
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ import (
|
||||||
"k8s.io/ingress/controllers/nginx/pkg/version"
|
"k8s.io/ingress/controllers/nginx/pkg/version"
|
||||||
"k8s.io/ingress/core/pkg/ingress"
|
"k8s.io/ingress/core/pkg/ingress"
|
||||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||||
|
"k8s.io/ingress/core/pkg/net/ssl"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -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{
|
content, err := n.t.Write(config.TemplateConfig{
|
||||||
ProxySetHeaders: setHeaders,
|
ProxySetHeaders: setHeaders,
|
||||||
MaxOpenFiles: maxOpenFiles,
|
MaxOpenFiles: maxOpenFiles,
|
||||||
|
|
|
@ -191,7 +191,7 @@ type Configuration struct {
|
||||||
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers
|
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers
|
||||||
SSLCiphers string `json:"ssl-ciphers,omitempty"`
|
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://www.openssl.org/docs/manmaster/apps/dhparam.html
|
||||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#DHE_handshake_and_dhparam
|
// 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
|
// http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
|
||||||
|
|
|
@ -175,25 +175,52 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchDHParamFile iterates all the secrets mounted inside the /etc/nginx-ssl directory
|
// AddOrUpdateDHParam creates a dh parameters file with the specified name
|
||||||
// in order to find a file with the name dhparam.pem. If such file exists it will
|
func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
|
||||||
// returns the path. If not it just returns an empty string
|
pemName := fmt.Sprintf("%v.pem", name)
|
||||||
func SearchDHParamFile(baseDir string) string {
|
pemFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, pemName)
|
||||||
files, _ := ioutil.ReadDir(baseDir)
|
|
||||||
for _, file := range files {
|
|
||||||
if !file.IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dhPath := fmt.Sprintf("%v/%v/dhparam.pem", baseDir, file.Name())
|
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
||||||
if _, err := os.Stat(dhPath); err == nil {
|
|
||||||
glog.Infof("using file '%v' for parameter ssl_dhparam", dhPath)
|
glog.V(3).Infof("Creating temp file %v for DH param: %v", tempPemFile.Name(), pemName)
|
||||||
return dhPath
|
if err != nil {
|
||||||
}
|
return "", fmt.Errorf("could not create temp pem file %v: %v", pemFileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Warning("no file dhparam.pem found in secrets")
|
_, err = tempPemFile.Write(dh)
|
||||||
return ""
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pemFileName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PemSHA1 returns the SHA1 of a pem file. This is used to
|
// PemSHA1 returns the SHA1 of a pem file. This is used to
|
||||||
|
|
79
examples/customization/ssl-dh-param/nginx/README.md
Normal file
79
examples/customization/ssl-dh-param/nginx/README.md
Normal file
|
@ -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`
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
dhparam.pem: "...base64 encoded data..."
|
||||||
|
kind: Secret
|
||||||
|
type: Opaque
|
||||||
|
metadata:
|
||||||
|
name: lb-dhparam
|
||||||
|
namespace: kube-system
|
Loading…
Reference in a new issue