Only support SSL dynamic mode
This commit is contained in:
parent
333d9fd48d
commit
80bd481abb
40 changed files with 415 additions and 709 deletions
26
.gitignore
vendored
26
.gitignore
vendored
|
@ -25,15 +25,6 @@
|
|||
Session.vim
|
||||
.netrwhist
|
||||
|
||||
# coverage artifacts
|
||||
.coverprofile
|
||||
/gover.coverprofile
|
||||
|
||||
e2e-tests
|
||||
|
||||
coverage.txt
|
||||
test/e2e/e2e\.test
|
||||
|
||||
# mkdocs
|
||||
site
|
||||
|
||||
|
@ -41,11 +32,18 @@ site
|
|||
gh-pages
|
||||
|
||||
# Docker-based builds
|
||||
/test/binaries
|
||||
/.env
|
||||
/.gocache/
|
||||
/bin/
|
||||
test/binaries
|
||||
|
||||
test/e2e-image/wait-for-nginx\.sh
|
||||
# coverage artifacts
|
||||
.coverprofile
|
||||
gover.coverprofile
|
||||
|
||||
e2e-tests
|
||||
coverage.txt
|
||||
test/e2e/e2e\.test
|
||||
.env
|
||||
.gocache/
|
||||
bin
|
||||
test/e2e-image/wait-for-nginx.sh
|
||||
.cache
|
||||
cover.out
|
||||
|
|
|
@ -63,7 +63,8 @@ kubectl create clusterrolebinding permissive-binding \
|
|||
--user=kubelet \
|
||||
--serviceaccount=default:ingress-nginx-e2e || true
|
||||
|
||||
until kubectl get secret | grep -q ^ingress-nginx-e2e-token; do \
|
||||
echo -e "${BGREEN}Waiting service account...${NC}"; \
|
||||
until kubectl get secret | grep -q -e ^ingress-nginx-e2e-token; do \
|
||||
echo -e "waiting for api token"; \
|
||||
sleep 3; \
|
||||
done
|
||||
|
|
|
@ -141,7 +141,7 @@ extension for this to succeed.`)
|
|||
`Customized address to set as the load-balancer status of Ingress objects this controller satisfies.
|
||||
Requires the update-status parameter.`)
|
||||
|
||||
enableDynamicCertificates = flags.Bool("enable-dynamic-certificates", true,
|
||||
_ = flags.Bool("enable-dynamic-certificates", true,
|
||||
`Dynamically update SSL certificates instead of reloading NGINX. Feature backed by OpenResty Lua libraries.`)
|
||||
|
||||
enableMetrics = flags.Bool("enable-metrics", true,
|
||||
|
@ -171,6 +171,8 @@ Takes the form "<host>:port". If not provided, no admission controller is starte
|
|||
flags.MarkDeprecated("status-port", `The status port is a unix socket now.`)
|
||||
flags.MarkDeprecated("force-namespace-isolation", `This flag doesn't do anything.`)
|
||||
|
||||
flags.MarkDeprecated("enable-dynamic-certificates", `Only dynamic mode is supported`)
|
||||
|
||||
flag.Set("logtostderr", "true")
|
||||
|
||||
flags.AddGoFlagSet(flag.CommandLine)
|
||||
|
@ -232,7 +234,6 @@ Takes the form "<host>:port". If not provided, no admission controller is starte
|
|||
}
|
||||
|
||||
ngx_config.EnableSSLChainCompletion = *enableSSLChainCompletion
|
||||
ngx_config.EnableDynamicCertificates = *enableDynamicCertificates
|
||||
|
||||
config := &controller.Configuration{
|
||||
APIServerHost: *apiserverHost,
|
||||
|
|
|
@ -39,7 +39,6 @@ import (
|
|||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
"k8s.io/ingress-nginx/internal/ingress/controller"
|
||||
"k8s.io/ingress-nginx/internal/ingress/metric"
|
||||
"k8s.io/ingress-nginx/internal/k8s"
|
||||
|
@ -63,13 +62,6 @@ func main() {
|
|||
klog.Fatal(err)
|
||||
}
|
||||
|
||||
nginxVersion()
|
||||
|
||||
fs, err := file.NewLocalFS()
|
||||
if err != nil {
|
||||
klog.Fatal(err)
|
||||
}
|
||||
|
||||
kubeClient, err := createApiserverClient(conf.APIServerHost, conf.KubeConfigFile)
|
||||
if err != nil {
|
||||
handleFatalInitError(err)
|
||||
|
@ -98,8 +90,8 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
conf.FakeCertificate = ssl.GetFakeSSLCert(fs)
|
||||
klog.Infof("Created fake certificate with PemFileName: %v", conf.FakeCertificate.PemFileName)
|
||||
conf.FakeCertificate = ssl.GetFakeSSLCert()
|
||||
klog.Infof("SSL fake certificate created %v", conf.FakeCertificate.PemFileName)
|
||||
|
||||
k8s.IsNetworkingIngressAvailable = k8s.NetworkingIngressAvailable(kubeClient)
|
||||
if !k8s.IsNetworkingIngressAvailable {
|
||||
|
@ -125,7 +117,7 @@ func main() {
|
|||
}
|
||||
mc.Start()
|
||||
|
||||
ngx := controller.NewNGINXController(conf, mc, fs)
|
||||
ngx := controller.NewNGINXController(conf, mc)
|
||||
go handleSigterm(ngx, func(code int) {
|
||||
os.Exit(code)
|
||||
})
|
||||
|
|
|
@ -19,6 +19,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -28,8 +29,8 @@ import (
|
|||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
"k8s.io/ingress-nginx/internal/ingress/controller"
|
||||
"k8s.io/ingress-nginx/internal/nginx"
|
||||
)
|
||||
|
||||
func TestCreateApiserverClient(t *testing.T) {
|
||||
|
@ -39,6 +40,15 @@ func TestCreateApiserverClient(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// the default value of nginx.TemplatePath assumes the template exists in
|
||||
// the root filesystem and not in the rootfs directory
|
||||
path, err := filepath.Abs(filepath.Join("../../rootfs/", nginx.TemplatePath))
|
||||
if err == nil {
|
||||
nginx.TemplatePath = path
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleSigterm(t *testing.T) {
|
||||
clientSet := fake.NewSimpleClientset()
|
||||
|
||||
|
@ -77,12 +87,7 @@ func TestHandleSigterm(t *testing.T) {
|
|||
}
|
||||
conf.Client = clientSet
|
||||
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
ngx := controller.NewNGINXController(conf, nil, fs)
|
||||
ngx := controller.NewNGINXController(conf, nil)
|
||||
|
||||
go handleSigterm(ngx, func(code int) {
|
||||
if code != 1 {
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func nginxVersion() {
|
||||
flag := "-v"
|
||||
|
||||
if klog.V(2) {
|
||||
flag = "-V"
|
||||
}
|
||||
|
||||
cmd := exec.Command("nginx", flag)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Run()
|
||||
}
|
|
@ -120,7 +120,7 @@ $ kubectl ingress-nginx backends -n ingress-nginx
|
|||
"secureCACert": {
|
||||
"secret": "",
|
||||
"caFilename": "",
|
||||
"pemSha": ""
|
||||
"caSha": ""
|
||||
},
|
||||
"sslPassthrough": false,
|
||||
"endpoints": [
|
||||
|
|
|
@ -16,92 +16,5 @@ limitations under the License.
|
|||
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
// ReadWriteByUser defines linux permission to read and write files for the owner user
|
||||
const ReadWriteByUser = 0660
|
||||
|
||||
// ReadByUserGroup defines linux permission to read files by the user and group owner/s
|
||||
const ReadByUserGroup = 0640
|
||||
|
||||
// Filesystem is an interface that we can use to mock various filesystem operations
|
||||
type Filesystem interface {
|
||||
filesystem.Filesystem
|
||||
}
|
||||
|
||||
// NewLocalFS implements Filesystem using same-named functions from "os" and "io/ioutil".
|
||||
func NewLocalFS() (Filesystem, error) {
|
||||
fs := filesystem.DefaultFs{}
|
||||
|
||||
for _, directory := range directories {
|
||||
err := fs.MkdirAll(directory, ReadWriteByUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// NewFakeFS creates an in-memory filesystem with all the required
|
||||
// paths used by the ingress controller.
|
||||
// This allows running test without polluting the local machine.
|
||||
func NewFakeFS() (Filesystem, error) {
|
||||
osFs := filesystem.DefaultFs{}
|
||||
fakeFs := filesystem.NewFakeFs()
|
||||
|
||||
//TODO: find another way to do this
|
||||
rootFS := filepath.Clean(fmt.Sprintf("%v/%v", os.Getenv("GOPATH"), "src/k8s.io/ingress-nginx/rootfs"))
|
||||
|
||||
var fileList []string
|
||||
err := filepath.Walk(rootFS, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
file := strings.TrimPrefix(path, rootFS)
|
||||
if file == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
fileList = append(fileList, file)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range fileList {
|
||||
realPath := fmt.Sprintf("%v%v", rootFS, file)
|
||||
|
||||
data, err := osFs.ReadFile(realPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fakeFile, err := fakeFs.Create(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = fakeFile.Write(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return fakeFs, nil
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewFakeFS(t *testing.T) {
|
||||
fs, err := NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating filesystem abstraction: %v", err)
|
||||
}
|
||||
|
||||
if fs == nil {
|
||||
t.Fatal("expected a filesystem but none returned")
|
||||
}
|
||||
|
||||
_, err = fs.Stat("/etc/nginx/nginx.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading default nginx.conf file: %v", err)
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ func (m mockCfg) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error)
|
|||
return &resolver.AuthSSLCert{
|
||||
Secret: name,
|
||||
CAFileName: "/opt/ca.pem",
|
||||
PemSHA: "123",
|
||||
CASHA: "123",
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
|
|
|
@ -77,7 +77,7 @@ func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, erro
|
|||
return &resolver.AuthSSLCert{
|
||||
Secret: "default/demo-secret",
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
PemSHA: "abc",
|
||||
CASHA: "abc",
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
@ -202,12 +202,12 @@ func TestEquals(t *testing.T) {
|
|||
sslCert1 := resolver.AuthSSLCert{
|
||||
Secret: "default/demo-secret",
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
PemSHA: "abc",
|
||||
CASHA: "abc",
|
||||
}
|
||||
sslCert2 := resolver.AuthSSLCert{
|
||||
Secret: "default/other-demo-secret",
|
||||
CAFileName: "/ssl/ca.crt",
|
||||
PemSHA: "abc",
|
||||
CASHA: "abc",
|
||||
}
|
||||
cfg1.AuthSSLCert = sslCert1
|
||||
cfg2.AuthSSLCert = sslCert2
|
||||
|
|
|
@ -18,6 +18,7 @@ package controller
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -63,7 +64,7 @@ func (n *NGINXController) Check(_ *http.Request) error {
|
|||
if err != nil {
|
||||
return errors.Wrap(err, "unexpected error reading /proc directory")
|
||||
}
|
||||
f, err := n.fileSystem.ReadFile(nginx.PID)
|
||||
f, err := ioutil.ReadFile(nginx.PID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unexpected error reading %v", nginx.PID)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||
|
@ -55,14 +54,10 @@ func TestNginxCheck(t *testing.T) {
|
|||
defer server.Close()
|
||||
server.Start()
|
||||
|
||||
// mock filesystem
|
||||
fs := filesystem.DefaultFs{}
|
||||
|
||||
n := &NGINXController{
|
||||
cfg: &Configuration{
|
||||
ListenPorts: &ngx_config.ListenPorts{},
|
||||
},
|
||||
fileSystem: fs,
|
||||
}
|
||||
|
||||
t.Run("no pid or process", func(t *testing.T) {
|
||||
|
@ -72,8 +67,8 @@ func TestNginxCheck(t *testing.T) {
|
|||
})
|
||||
|
||||
// create pid file
|
||||
fs.MkdirAll("/tmp", file.ReadWriteByUser)
|
||||
pidFile, err := fs.Create(nginx.PID)
|
||||
os.MkdirAll("/tmp", file.ReadWriteByUser)
|
||||
pidFile, err := os.Create(nginx.PID)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
|
@ -33,8 +33,6 @@ import (
|
|||
var (
|
||||
// EnableSSLChainCompletion Autocomplete SSL certificate chains with missing intermediate CA certificates.
|
||||
EnableSSLChainCompletion = false
|
||||
// EnableDynamicCertificates Dynamically update SSL certificates instead of reloading NGINX
|
||||
EnableDynamicCertificates = true
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -605,6 +603,10 @@ type Configuration struct {
|
|||
|
||||
// Lua shared dict configuration data / certificate data
|
||||
LuaSharedDicts map[string]int `json:"lua-shared-dicts"`
|
||||
|
||||
// DefaultSSLCertificate holds the default SSL certificate to use in the configuration
|
||||
// It can be the fake certificate or the one behind the flag --default-ssl-certificate
|
||||
DefaultSSLCertificate *ingress.SSLCert `json:"-"`
|
||||
}
|
||||
|
||||
// NewDefault returns the default nginx configuration
|
||||
|
@ -781,7 +783,6 @@ type TemplateConfig struct {
|
|||
RedirectServers interface{}
|
||||
ListenPorts *ListenPorts
|
||||
PublishService *apiv1.Service
|
||||
EnableDynamicCertificates bool
|
||||
EnableMetrics bool
|
||||
|
||||
PID string
|
||||
|
|
|
@ -886,19 +886,18 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
|
|||
return upstreams, nil
|
||||
}
|
||||
|
||||
// overridePemFileNameAndPemSHA should only be called when EnableDynamicCertificates
|
||||
// ideally this function should not exist, the only reason why we use it is that
|
||||
// we rely on PemFileName in nginx.tmpl to configure SSL directives
|
||||
// and PemSHA to force reload
|
||||
func (n *NGINXController) overridePemFileNameAndPemSHA(cert *ingress.SSLCert) {
|
||||
// TODO(elvinefendi): It is not great but we currently use PemFileName to decide whether SSL needs to be configured
|
||||
// in nginx configuration or not. The whole thing needs to be refactored, we should rely on a proper
|
||||
// signal to configure SSL, not PemFileName.
|
||||
cert.PemFileName = n.cfg.FakeCertificate.PemFileName
|
||||
func (n *NGINXController) getDefaultSSLCertificate() *ingress.SSLCert {
|
||||
// read custom default SSL certificate, fall back to generated default certificate
|
||||
if n.cfg.DefaultSSLCertificate != "" {
|
||||
certificate, err := n.store.GetLocalSSLCert(n.cfg.DefaultSSLCertificate)
|
||||
if err == nil {
|
||||
return certificate
|
||||
}
|
||||
|
||||
// TODO(elvinefendi): This is again another hacky way of avoiding Nginx reload when certificate
|
||||
// changes in dynamic SSL mode since FakeCertificate never changes.
|
||||
cert.PemSHA = n.cfg.FakeCertificate.PemSHA
|
||||
klog.Warningf("Error loading custom default certificate, falling back to generated default:\n%v", err)
|
||||
}
|
||||
|
||||
return n.cfg.FakeCertificate
|
||||
}
|
||||
|
||||
// createServers builds a map of host name to Server structs from a map of
|
||||
|
@ -930,25 +929,10 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
ProxyHTTPVersion: bdef.ProxyHTTPVersion,
|
||||
}
|
||||
|
||||
defaultCertificate := n.cfg.FakeCertificate
|
||||
|
||||
// read custom default SSL certificate, fall back to generated default certificate
|
||||
if n.cfg.DefaultSSLCertificate != "" {
|
||||
certificate, err := n.store.GetLocalSSLCert(n.cfg.DefaultSSLCertificate)
|
||||
if err == nil {
|
||||
defaultCertificate = certificate
|
||||
if ngx_config.EnableDynamicCertificates {
|
||||
n.overridePemFileNameAndPemSHA(defaultCertificate)
|
||||
}
|
||||
} else {
|
||||
klog.Warningf("Error loading custom default certificate, falling back to generated default:\n%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// initialize default server and root location
|
||||
servers[defServerName] = &ingress.Server{
|
||||
Hostname: defServerName,
|
||||
SSLCert: *defaultCertificate,
|
||||
SSLCert: n.getDefaultSSLCertificate(),
|
||||
Locations: []*ingress.Location{
|
||||
{
|
||||
Path: rootLocation,
|
||||
|
@ -1012,6 +996,7 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
if host == "" {
|
||||
host = defServerName
|
||||
}
|
||||
|
||||
if _, ok := servers[host]; ok {
|
||||
// server already configured
|
||||
continue
|
||||
|
@ -1079,7 +1064,7 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
}
|
||||
|
||||
// only add a certificate if the server does not have one previously configured
|
||||
if servers[host].SSLCert.PemFileName != "" {
|
||||
if servers[host].SSLCert != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1089,10 +1074,8 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
}
|
||||
|
||||
tlsSecretName := extractTLSSecretName(host, ing, n.store.GetLocalSSLCert)
|
||||
|
||||
if tlsSecretName == "" {
|
||||
klog.V(3).Infof("Host %q is listed in the TLS section but secretName is empty. Using default certificate.", host)
|
||||
servers[host].SSLCert = *defaultCertificate
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1083,6 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
cert, err := n.store.GetLocalSSLCert(secrKey)
|
||||
if err != nil {
|
||||
klog.Warningf("Error getting SSL certificate %q: %v. Using default certificate", secrKey, err)
|
||||
servers[host].SSLCert = *defaultCertificate
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1115,16 +1097,11 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
|
|||
klog.Warningf("SSL certificate %q does not contain a Common Name or Subject Alternative Name for server %q: %v",
|
||||
secrKey, host, err)
|
||||
klog.Warningf("Using default certificate")
|
||||
servers[host].SSLCert = *defaultCertificate
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if ngx_config.EnableDynamicCertificates {
|
||||
n.overridePemFileNameAndPemSHA(cert)
|
||||
}
|
||||
|
||||
servers[host].SSLCert = *cert
|
||||
servers[host].SSLCert = cert
|
||||
|
||||
if cert.ExpireTime.Before(time.Now().Add(240 * time.Hour)) {
|
||||
klog.Warningf("SSL certificate for server %q is about to expire (%v)", host, cert.ExpireTime)
|
||||
|
|
|
@ -37,7 +37,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/canary"
|
||||
|
@ -1109,11 +1108,6 @@ func newNGINXController(t *testing.T) *NGINXController {
|
|||
t.Fatalf("error creating the configuration map: %v", err)
|
||||
}
|
||||
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
|
||||
storer := store.New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -1122,12 +1116,11 @@ func newNGINXController(t *testing.T) *NGINXController {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
channels.NewRingChannel(10),
|
||||
pod,
|
||||
false)
|
||||
|
||||
sslCert := ssl.GetFakeSSLCert(fs)
|
||||
sslCert := ssl.GetFakeSSLCert()
|
||||
config := &Configuration{
|
||||
FakeCertificate: sslCert,
|
||||
ListenPorts: &ngx_config.ListenPorts{
|
||||
|
@ -1139,7 +1132,6 @@ func newNGINXController(t *testing.T) *NGINXController {
|
|||
store: storer,
|
||||
cfg: config,
|
||||
command: NewNginxCommand(),
|
||||
fileSystem: fs,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ import (
|
|||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||
|
||||
adm_controler "k8s.io/ingress-nginx/internal/admission/controller"
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
|
@ -69,12 +68,8 @@ const (
|
|||
tempNginxPattern = "nginx-cfg"
|
||||
)
|
||||
|
||||
var (
|
||||
tmplPath = "/etc/nginx/template/nginx.tmpl"
|
||||
)
|
||||
|
||||
// NewNGINXController creates a new NGINX Ingress controller.
|
||||
func NewNGINXController(config *Configuration, mc metric.Collector, fs file.Filesystem) *NGINXController {
|
||||
func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXController {
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(klog.Infof)
|
||||
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{
|
||||
|
@ -102,8 +97,6 @@ func NewNGINXController(config *Configuration, mc metric.Collector, fs file.File
|
|||
|
||||
stopLock: &sync.Mutex{},
|
||||
|
||||
fileSystem: fs,
|
||||
|
||||
runningConfig: new(ingress.Configuration),
|
||||
|
||||
Proxy: &TCPProxy{},
|
||||
|
@ -135,7 +128,6 @@ func NewNGINXController(config *Configuration, mc metric.Collector, fs file.File
|
|||
config.DefaultSSLCertificate,
|
||||
config.ResyncPeriod,
|
||||
config.Client,
|
||||
fs,
|
||||
n.updateCh,
|
||||
pod,
|
||||
config.DisableCatchAll)
|
||||
|
@ -156,7 +148,7 @@ func NewNGINXController(config *Configuration, mc metric.Collector, fs file.File
|
|||
}
|
||||
|
||||
onTemplateChange := func() {
|
||||
template, err := ngx_template.NewTemplate(tmplPath, fs)
|
||||
template, err := ngx_template.NewTemplate(nginx.TemplatePath)
|
||||
if err != nil {
|
||||
// this error is different from the rest because it must be clear why nginx is not working
|
||||
klog.Errorf(`
|
||||
|
@ -172,21 +164,16 @@ Error loading new template: %v
|
|||
n.syncQueue.EnqueueTask(task.GetDummyObject("template-change"))
|
||||
}
|
||||
|
||||
ngxTpl, err := ngx_template.NewTemplate(tmplPath, fs)
|
||||
ngxTpl, err := ngx_template.NewTemplate(nginx.TemplatePath)
|
||||
if err != nil {
|
||||
klog.Fatalf("Invalid NGINX configuration template: %v", err)
|
||||
}
|
||||
|
||||
n.t = ngxTpl
|
||||
|
||||
if _, ok := fs.(filesystem.DefaultFs); !ok {
|
||||
// do not setup watchers on tests
|
||||
return n
|
||||
}
|
||||
|
||||
_, err = watch.NewFileWatcher(tmplPath, onTemplateChange)
|
||||
_, err = watch.NewFileWatcher(nginx.TemplatePath, onTemplateChange)
|
||||
if err != nil {
|
||||
klog.Fatalf("Error creating file watcher for %v: %v", tmplPath, err)
|
||||
klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, err)
|
||||
}
|
||||
|
||||
filesToWatch := []string{}
|
||||
|
@ -260,8 +247,6 @@ type NGINXController struct {
|
|||
|
||||
store store.Storer
|
||||
|
||||
fileSystem filesystem.Filesystem
|
||||
|
||||
metricCollector metric.Collector
|
||||
|
||||
validationWebhookServer *http.Server
|
||||
|
@ -582,7 +567,7 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
nsSecName := strings.Replace(secretName, "/", "-", -1)
|
||||
dh, ok := secret.Data["dhparam.pem"]
|
||||
if ok {
|
||||
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh, n.fileSystem)
|
||||
pemFileName, err := ssl.AddOrUpdateDHParam(nsSecName, dh)
|
||||
if err != nil {
|
||||
klog.Warningf("Error adding or updating dhparam file %v: %v", nsSecName, err)
|
||||
} else {
|
||||
|
@ -594,6 +579,8 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
|
||||
cfg.SSLDHParam = sslDHParam
|
||||
|
||||
cfg.DefaultSSLCertificate = n.getDefaultSSLCertificate()
|
||||
|
||||
tc := ngx_config.TemplateConfig{
|
||||
ProxySetHeaders: setHeaders,
|
||||
AddHeaders: addHeaders,
|
||||
|
@ -611,7 +598,6 @@ func (n NGINXController) generateTemplate(cfg ngx_config.Configuration, ingressC
|
|||
IsSSLPassthroughEnabled: n.cfg.EnableSSLPassthrough,
|
||||
ListenPorts: n.cfg.ListenPorts,
|
||||
PublishService: n.GetPublishService(),
|
||||
EnableDynamicCertificates: ngx_config.EnableDynamicCertificates,
|
||||
EnableMetrics: n.cfg.EnableMetrics,
|
||||
|
||||
HealthzURI: nginx.HealthPath,
|
||||
|
@ -805,7 +791,9 @@ func clearCertificates(config *ingress.Configuration) {
|
|||
var clearedServers []*ingress.Server
|
||||
for _, server := range config.Servers {
|
||||
copyOfServer := *server
|
||||
copyOfServer.SSLCert = ingress.SSLCert{PemFileName: copyOfServer.SSLCert.PemFileName}
|
||||
if copyOfServer.SSLCert != nil {
|
||||
copyOfServer.SSLCert = &ingress.SSLCert{PemFileName: copyOfServer.SSLCert.PemFileName}
|
||||
}
|
||||
clearedServers = append(clearedServers, ©OfServer)
|
||||
}
|
||||
config.Servers = clearedServers
|
||||
|
@ -853,10 +841,8 @@ func (n *NGINXController) IsDynamicConfigurationEnough(pcfg *ingress.Configurati
|
|||
copyOfRunningConfig.ControllerPodsCount = 0
|
||||
copyOfPcfg.ControllerPodsCount = 0
|
||||
|
||||
if ngx_config.EnableDynamicCertificates {
|
||||
clearCertificates(©OfRunningConfig)
|
||||
clearCertificates(©OfPcfg)
|
||||
}
|
||||
|
||||
return copyOfRunningConfig.Equal(©OfPcfg)
|
||||
}
|
||||
|
@ -951,12 +937,10 @@ func configureDynamically(pcfg *ingress.Configuration) error {
|
|||
return fmt.Errorf("unexpected error code: %d", statusCode)
|
||||
}
|
||||
|
||||
if ngx_config.EnableDynamicCertificates {
|
||||
err = configureCertificates(pcfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -988,16 +972,16 @@ func updateStreamConfiguration(streams []ingress.Backend) error {
|
|||
// configureCertificates JSON encodes certificates and POSTs it to an internal HTTP endpoint
|
||||
// that is handled by Lua
|
||||
func configureCertificates(pcfg *ingress.Configuration) error {
|
||||
var servers []*ingress.Server
|
||||
servers := make([]*ingress.Server, 0)
|
||||
|
||||
for _, server := range pcfg.Servers {
|
||||
if server.SSLCert.PemCertKey == "" {
|
||||
if server.SSLCert == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
servers = append(servers, &ingress.Server{
|
||||
Hostname: server.Hostname,
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
PemCertKey: server.SSLCert.PemCertKey,
|
||||
},
|
||||
})
|
||||
|
@ -1005,7 +989,7 @@ func configureCertificates(pcfg *ingress.Configuration) error {
|
|||
if server.Alias != "" && ssl.IsValidHostname(server.Alias, server.SSLCert.CN) {
|
||||
servers = append(servers, &ingress.Server{
|
||||
Hostname: server.Alias,
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
PemCertKey: server.SSLCert.PemCertKey,
|
||||
},
|
||||
})
|
||||
|
@ -1014,13 +998,13 @@ func configureCertificates(pcfg *ingress.Configuration) error {
|
|||
|
||||
redirects := buildRedirects(pcfg.Servers)
|
||||
for _, redirect := range redirects {
|
||||
if redirect.SSLCert.PemCertKey == "" {
|
||||
if redirect.SSLCert == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
servers = append(servers, &ingress.Server{
|
||||
Hostname: redirect.From,
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
PemCertKey: redirect.SSLCert.PemCertKey,
|
||||
},
|
||||
})
|
||||
|
@ -1131,7 +1115,7 @@ func cleanTempNginxCfg() error {
|
|||
type redirect struct {
|
||||
From string
|
||||
To string
|
||||
SSLCert ingress.SSLCert
|
||||
SSLCert *ingress.SSLCert
|
||||
}
|
||||
|
||||
func buildRedirects(servers []*ingress.Server) []*redirect {
|
||||
|
@ -1175,7 +1159,7 @@ func buildRedirects(servers []*ingress.Server) []*redirect {
|
|||
To: to,
|
||||
}
|
||||
|
||||
if srv.SSLCert.PemSHA != "" {
|
||||
if srv.SSLCert != nil {
|
||||
if ssl.IsValidHostname(from, srv.SSLCert.CN) {
|
||||
r.SSLCert = srv.SSLCert
|
||||
} else {
|
||||
|
|
|
@ -32,14 +32,10 @@ import (
|
|||
apiv1 "k8s.io/api/core/v1"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||
"k8s.io/ingress-nginx/internal/nginx"
|
||||
)
|
||||
|
||||
func TestIsDynamicConfigurationEnough(t *testing.T) {
|
||||
ngx_config.EnableDynamicCertificates = false
|
||||
defer func() { ngx_config.EnableDynamicCertificates = true }()
|
||||
|
||||
backends := []*ingress.Backend{{
|
||||
Name: "fakenamespace-myapp-80",
|
||||
Endpoints: []ingress.Endpoint{
|
||||
|
@ -62,7 +58,7 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
|
|||
Backend: "fakenamespace-myapp-80",
|
||||
},
|
||||
},
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
PemCertKey: "fake-certificate",
|
||||
},
|
||||
}}
|
||||
|
@ -98,8 +94,6 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
|
|||
Servers: servers,
|
||||
}
|
||||
|
||||
ngx_config.EnableDynamicCertificates = true
|
||||
|
||||
if !n.IsDynamicConfigurationEnough(newConfig) {
|
||||
t.Errorf("Expected to be dynamically configurable when only backends change")
|
||||
}
|
||||
|
@ -112,7 +106,7 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
|
|||
Backend: "fakenamespace-myapp-80",
|
||||
},
|
||||
},
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
PemCertKey: "fake-certificate",
|
||||
},
|
||||
}}
|
||||
|
@ -201,6 +195,12 @@ func TestConfigureDynamically(t *testing.T) {
|
|||
t.Errorf("controllerPodsCount should be present in JSON content: %v", body)
|
||||
}
|
||||
}
|
||||
case "/configuration/servers":
|
||||
{
|
||||
if !strings.Contains(body, "[]") {
|
||||
t.Errorf("controllerPodsCount should be present in JSON content: %v", body)
|
||||
}
|
||||
}
|
||||
default:
|
||||
t.Errorf("unknown request to %s", r.URL.Path)
|
||||
}
|
||||
|
@ -246,9 +246,6 @@ func TestConfigureDynamically(t *testing.T) {
|
|||
ControllerPodsCount: 2,
|
||||
}
|
||||
|
||||
ngx_config.EnableDynamicCertificates = false
|
||||
defer func() { ngx_config.EnableDynamicCertificates = true }()
|
||||
|
||||
err = configureDynamically(commonConfig)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error posting dynamic configuration: %v", err)
|
||||
|
@ -276,7 +273,7 @@ func TestConfigureCertificates(t *testing.T) {
|
|||
|
||||
servers := []*ingress.Server{{
|
||||
Hostname: "myapp.fake",
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
PemCertKey: "fake-cert",
|
||||
},
|
||||
}}
|
||||
|
|
|
@ -17,17 +17,20 @@ limitations under the License.
|
|||
package store
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
networking "k8s.io/api/networking/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||
"k8s.io/ingress-nginx/internal/net/ssl"
|
||||
)
|
||||
|
||||
|
@ -39,7 +42,6 @@ func (s *k8sStore) syncSecret(key string) {
|
|||
|
||||
klog.V(3).Infof("Syncing Secret %q", key)
|
||||
|
||||
// TODO: getPemCertificate should not write to disk to avoid unnecessary overhead
|
||||
cert, err := s.getPemCertificate(key)
|
||||
if err != nil {
|
||||
if !isErrSecretForAuth(err) {
|
||||
|
@ -92,6 +94,7 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
if cert == nil {
|
||||
return nil, fmt.Errorf("key 'tls.crt' missing from Secret %q", secretName)
|
||||
}
|
||||
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf("key 'tls.key' missing from Secret %q", secretName)
|
||||
}
|
||||
|
@ -101,15 +104,16 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
|
||||
}
|
||||
|
||||
if !ngx_config.EnableDynamicCertificates || len(ca) > 0 {
|
||||
err = ssl.StoreSSLCertOnDisk(s.filesystem, nsSecName, sslCert)
|
||||
if len(ca) > 0 {
|
||||
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while storing certificate and key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ca) > 0 {
|
||||
err = ssl.ConfigureCACertWithCertAndKey(s.filesystem, nsSecName, ca, sslCert)
|
||||
sslCert.CAFileName = path
|
||||
sslCert.CASHA = file.SHA1(path)
|
||||
|
||||
err = ssl.ConfigureCACertWithCertAndKey(nsSecName, ca, sslCert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error configuring CA certificate: %v", err)
|
||||
}
|
||||
|
@ -120,14 +124,13 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
msg += " and authentication"
|
||||
}
|
||||
klog.V(3).Info(msg)
|
||||
|
||||
} else if len(ca) > 0 {
|
||||
sslCert, err = ssl.CreateCACert(ca)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected error creating SSL Cert: %v", err)
|
||||
}
|
||||
|
||||
err = ssl.ConfigureCACert(s.filesystem, nsSecName, ca, sslCert)
|
||||
err = ssl.ConfigureCACert(nsSecName, ca, sslCert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error configuring CA certificate: %v", err)
|
||||
}
|
||||
|
@ -135,7 +138,6 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
// makes this secret in 'syncSecret' to be used for Certificate Authentication
|
||||
// this does not enable Certificate Authentication
|
||||
klog.V(3).Infof("Configuring Secret %q for TLS authentication", secretName)
|
||||
|
||||
} else {
|
||||
if auth != nil {
|
||||
return nil, ErrSecretForAuth
|
||||
|
@ -147,6 +149,21 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
|
|||
sslCert.Name = secret.Name
|
||||
sslCert.Namespace = secret.Namespace
|
||||
|
||||
hasher := sha1.New()
|
||||
hasher.Write(sslCert.Certificate.Raw)
|
||||
|
||||
sslCert.PemSHA = hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
// the default SSL certificate needs to be present on disk
|
||||
if secretName == s.defaultSSLCertificate {
|
||||
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "storing default SSL Certificate")
|
||||
}
|
||||
|
||||
sslCert.PemFileName = path
|
||||
}
|
||||
|
||||
return sslCert, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -152,9 +152,9 @@ func (e NotExistsError) Error() string {
|
|||
|
||||
// Run initiates the synchronization of the informers against the API server.
|
||||
func (i *Informer) Run(stopCh chan struct{}) {
|
||||
go i.Secret.Run(stopCh)
|
||||
go i.Endpoint.Run(stopCh)
|
||||
go i.Service.Run(stopCh)
|
||||
go i.Secret.Run(stopCh)
|
||||
go i.ConfigMap.Run(stopCh)
|
||||
go i.Pod.Run(stopCh)
|
||||
|
||||
|
@ -165,6 +165,7 @@ func (i *Informer) Run(stopCh chan struct{}) {
|
|||
i.Service.HasSynced,
|
||||
i.Secret.HasSynced,
|
||||
i.ConfigMap.HasSynced,
|
||||
i.Pod.HasSynced,
|
||||
) {
|
||||
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
|
||||
}
|
||||
|
@ -208,8 +209,6 @@ type k8sStore struct {
|
|||
// secret in the annotations.
|
||||
secretIngressMap ObjectRefMap
|
||||
|
||||
filesystem file.Filesystem
|
||||
|
||||
// updateCh
|
||||
updateCh *channels.RingChannel
|
||||
|
||||
|
@ -229,7 +228,6 @@ func New(
|
|||
namespace, configmap, tcp, udp, defaultSSLCertificate string,
|
||||
resyncPeriod time.Duration,
|
||||
client clientset.Interface,
|
||||
fs file.Filesystem,
|
||||
updateCh *channels.RingChannel,
|
||||
pod *k8s.PodInfo,
|
||||
disableCatchAll bool) Storer {
|
||||
|
@ -238,7 +236,6 @@ func New(
|
|||
informers: &Informer{},
|
||||
listers: &Lister{},
|
||||
sslStore: NewSSLCertTracker(),
|
||||
filesystem: fs,
|
||||
updateCh: updateCh,
|
||||
backendConfig: ngx_config.NewDefault(),
|
||||
syncSecretMu: &sync.Mutex{},
|
||||
|
@ -494,6 +491,7 @@ func New(
|
|||
}
|
||||
store.syncIngress(ing)
|
||||
}
|
||||
|
||||
updateCh.In() <- Event{
|
||||
Type: DeleteEvent,
|
||||
Obj: obj,
|
||||
|
@ -813,7 +811,7 @@ func (s *k8sStore) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error
|
|||
return &resolver.AuthSSLCert{
|
||||
Secret: name,
|
||||
CAFileName: cert.CAFileName,
|
||||
PemSHA: cert.PemSHA,
|
||||
CASHA: cert.CASHA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,8 @@ import (
|
|||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||
"k8s.io/ingress-nginx/internal/k8s"
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
@ -87,7 +85,6 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
fs := newFS(t)
|
||||
storer := New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -96,7 +93,6 @@ func TestStore(t *testing.T) {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
updateCh,
|
||||
pod,
|
||||
false)
|
||||
|
@ -167,7 +163,6 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
fs := newFS(t)
|
||||
storer := New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -176,7 +171,6 @@ func TestStore(t *testing.T) {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
updateCh,
|
||||
pod,
|
||||
false)
|
||||
|
@ -317,7 +311,6 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
fs := newFS(t)
|
||||
storer := New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -326,7 +319,6 @@ func TestStore(t *testing.T) {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
updateCh,
|
||||
pod,
|
||||
false)
|
||||
|
@ -423,7 +415,6 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
fs := newFS(t)
|
||||
storer := New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -432,7 +423,6 @@ func TestStore(t *testing.T) {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
updateCh,
|
||||
pod,
|
||||
false)
|
||||
|
@ -512,7 +502,6 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
fs := newFS(t)
|
||||
storer := New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -521,7 +510,6 @@ func TestStore(t *testing.T) {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
updateCh,
|
||||
pod,
|
||||
false)
|
||||
|
@ -623,7 +611,6 @@ func TestStore(t *testing.T) {
|
|||
}
|
||||
}(updateCh)
|
||||
|
||||
fs := newFS(t)
|
||||
storer := New(
|
||||
ns,
|
||||
fmt.Sprintf("%v/config", ns),
|
||||
|
@ -632,7 +619,6 @@ func TestStore(t *testing.T) {
|
|||
"",
|
||||
10*time.Minute,
|
||||
clientSet,
|
||||
fs,
|
||||
updateCh,
|
||||
pod,
|
||||
false)
|
||||
|
@ -701,39 +687,6 @@ func TestStore(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("error creating secret: %v", err)
|
||||
}
|
||||
|
||||
t.Run("should exists a secret in the local store and filesystem", func(t *testing.T) {
|
||||
ngx_config.EnableDynamicCertificates = false
|
||||
defer func() { ngx_config.EnableDynamicCertificates = true }()
|
||||
|
||||
err := framework.WaitForSecretInNamespace(clientSet, ns, name)
|
||||
if err != nil {
|
||||
t.Errorf("error waiting for secret: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
pemFile := fmt.Sprintf("%v/%v-%v.pem", file.DefaultSSLDirectory, ns, name)
|
||||
err = framework.WaitForFileInFS(pemFile, fs)
|
||||
if err != nil {
|
||||
t.Errorf("error waiting for file to exist on the file system: %v", err)
|
||||
}
|
||||
|
||||
secretName := fmt.Sprintf("%v/%v", ns, name)
|
||||
sslCert, err := storer.GetLocalSSLCert(secretName)
|
||||
if err != nil {
|
||||
t.Errorf("error reading local secret %v: %v", secretName, err)
|
||||
}
|
||||
|
||||
if sslCert == nil {
|
||||
t.Errorf("expected a secret but none returned")
|
||||
}
|
||||
|
||||
pemSHA := file.SHA1(pemFile)
|
||||
if sslCert.PemSHA != pemSHA {
|
||||
t.Errorf("SHA of secret on disk differs from local secret store (%v != %v)", pemSHA, sslCert.PemSHA)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// test add ingress with secret it doesn't exists and then add secret
|
||||
|
@ -821,22 +774,9 @@ func deleteIngress(ingress *networking.Ingress, clientSet kubernetes.Interface,
|
|||
t.Logf("Ingress %+v deleted", ingress)
|
||||
}
|
||||
|
||||
func newFS(t *testing.T) file.Filesystem {
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("error creating filesystem: %v", err)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
// newStore creates a new mock object store for tests which do not require the
|
||||
// use of Informers.
|
||||
func newStore(t *testing.T) *k8sStore {
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
|
||||
pod := &k8s.PodInfo{
|
||||
Name: "ingress-1",
|
||||
Namespace: v1.NamespaceDefault,
|
||||
|
@ -853,7 +793,6 @@ func newStore(t *testing.T) *k8sStore {
|
|||
Pod: PodLister{cache.NewStore(cache.MetaNamespaceKeyFunc)},
|
||||
},
|
||||
sslStore: NewSSLCertTracker(),
|
||||
filesystem: fs,
|
||||
updateCh: channels.NewRingChannel(10),
|
||||
syncSecretMu: new(sync.Mutex),
|
||||
backendConfigMu: new(sync.RWMutex),
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
|
@ -39,7 +40,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
|
||||
|
@ -67,8 +67,8 @@ type Template struct {
|
|||
|
||||
//NewTemplate returns a new Template instance or an
|
||||
//error if the specified template file contains errors
|
||||
func NewTemplate(file string, fs file.Filesystem) (*Template, error) {
|
||||
data, err := fs.ReadFile(file)
|
||||
func NewTemplate(file string) (*Template, error) {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unexpected error reading template %v", file)
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ func locationConfigForLua(l interface{}, s interface{}, a interface{}) string {
|
|||
return "{}"
|
||||
}
|
||||
|
||||
forceSSLRedirect := location.Rewrite.ForceSSLRedirect || len(server.SSLCert.PemFileName) > 0 && location.Rewrite.SSLRedirect
|
||||
forceSSLRedirect := location.Rewrite.ForceSSLRedirect || (server.SSLCert != nil && location.Rewrite.SSLRedirect)
|
||||
forceSSLRedirect = forceSSLRedirect && !isLocationInLocationList(l, all.Cfg.NoTLSRedirectLocations)
|
||||
|
||||
return fmt.Sprintf(`{
|
||||
|
@ -1177,6 +1177,12 @@ func buildHTTPSListener(t interface{}, s interface{}) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
/*
|
||||
if server.SSLCert == nil && server.Hostname != "_" {
|
||||
return ""
|
||||
}
|
||||
*/
|
||||
|
||||
co := commonListenOptions(tc, hostname)
|
||||
|
||||
addrV4 := []string{""}
|
||||
|
|
|
@ -17,20 +17,20 @@ limitations under the License.
|
|||
package template
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
networking "k8s.io/api/networking/v1beta1"
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/ingress"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/authreq"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
|
||||
|
@ -39,8 +39,18 @@ import (
|
|||
"k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/rewrite"
|
||||
"k8s.io/ingress-nginx/internal/ingress/controller/config"
|
||||
"k8s.io/ingress-nginx/internal/nginx"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// the default value of nginx.TemplatePath assumes the template exists in
|
||||
// the root filesystem and not in the rootfs directory
|
||||
path, err := filepath.Abs(filepath.Join("../../../../rootfs/", nginx.TemplatePath))
|
||||
if err == nil {
|
||||
nginx.TemplatePath = path
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: add tests for SSLPassthrough
|
||||
tmplFuncTestcases = map[string]struct {
|
||||
|
@ -435,16 +445,13 @@ func TestTemplateWithData(t *testing.T) {
|
|||
dat.ListenPorts = &config.ListenPorts{}
|
||||
}
|
||||
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
ngxTpl, err := NewTemplate("/etc/nginx/template/nginx.tmpl", fs)
|
||||
ngxTpl, err := NewTemplate(nginx.TemplatePath)
|
||||
if err != nil {
|
||||
t.Errorf("invalid NGINX template: %v", err)
|
||||
}
|
||||
|
||||
dat.Cfg.DefaultSSLCertificate = &ingress.SSLCert{}
|
||||
|
||||
rt, err := ngxTpl.Write(dat)
|
||||
if err != nil {
|
||||
t.Errorf("invalid NGINX template: %v", err)
|
||||
|
@ -479,12 +486,7 @@ func BenchmarkTemplateWithData(b *testing.B) {
|
|||
b.Errorf("unexpected error unmarshalling json: %v", err)
|
||||
}
|
||||
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
b.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
ngxTpl, err := NewTemplate("/etc/nginx/template/nginx.tmpl", fs)
|
||||
ngxTpl, err := NewTemplate(nginx.TemplatePath)
|
||||
if err != nil {
|
||||
b.Errorf("invalid NGINX template: %v", err)
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ func (cm Controller) Collect(ch chan<- prometheus.Metric) {
|
|||
// SetSSLExpireTime sets the expiration time of SSL Certificates
|
||||
func (cm *Controller) SetSSLExpireTime(servers []*ingress.Server) {
|
||||
for _, s := range servers {
|
||||
if s.Hostname != "" && s.SSLCert.ExpireTime.Unix() > 0 {
|
||||
if s.Hostname != "" && s.SSLCert != nil && s.SSLCert.ExpireTime.Unix() > 0 {
|
||||
labels := make(prometheus.Labels, len(cm.labels)+1)
|
||||
for k, v := range cm.labels {
|
||||
labels[k] = v
|
||||
|
|
|
@ -80,13 +80,13 @@ func TestControllerCounters(t *testing.T) {
|
|||
servers := []*ingress.Server{
|
||||
{
|
||||
Hostname: "demo",
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
ExpireTime: t1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Hostname: "invalid",
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
ExpireTime: time.Unix(0, 0),
|
||||
},
|
||||
},
|
||||
|
@ -135,13 +135,13 @@ func TestRemoveMetrics(t *testing.T) {
|
|||
servers := []*ingress.Server{
|
||||
{
|
||||
Hostname: "demo",
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
ExpireTime: t1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Hostname: "invalid",
|
||||
SSLCert: ingress.SSLCert{
|
||||
SSLCert: &ingress.SSLCert{
|
||||
ExpireTime: time.Unix(0, 0),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -51,8 +51,8 @@ type AuthSSLCert struct {
|
|||
Secret string `json:"secret"`
|
||||
// CAFileName contains the path to the secrets 'ca.crt'
|
||||
CAFileName string `json:"caFilename"`
|
||||
// PemSHA contains the SHA1 hash of the 'ca.crt' or combinations of (tls.crt, tls.key, tls.crt) depending on certs in secret
|
||||
PemSHA string `json:"pemSha"`
|
||||
// CASHA contains the SHA1 hash of the 'ca.crt' or combinations of (tls.crt, tls.key, tls.crt) depending on certs in secret
|
||||
CASHA string `json:"caSha"`
|
||||
}
|
||||
|
||||
// Equal tests for equality between two AuthSSLCert types
|
||||
|
@ -70,7 +70,7 @@ func (asslc1 *AuthSSLCert) Equal(assl2 *AuthSSLCert) bool {
|
|||
if asslc1.CAFileName != assl2.CAFileName {
|
||||
return false
|
||||
}
|
||||
if asslc1.PemSHA != assl2.PemSHA {
|
||||
if asslc1.CASHA != assl2.CASHA {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -20,25 +20,36 @@ import (
|
|||
"crypto/x509"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// SSLCert describes a SSL certificate to be used in a server
|
||||
type SSLCert struct {
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Certificate *x509.Certificate `json:"certificate,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
Certificate *x509.Certificate `json:"-"`
|
||||
|
||||
// CAFileName contains the path to the file with the root certificate
|
||||
CAFileName string `json:"caFileName"`
|
||||
|
||||
// CASHA contains the sha1 of the ca file.
|
||||
// This is used to detect changes in the secret that contains certificates
|
||||
CASHA string `json:"caSha"`
|
||||
|
||||
// PemFileName contains the path to the file with the certificate and key concatenated
|
||||
PemFileName string `json:"pemFileName"`
|
||||
|
||||
// PemSHA contains the sha1 of the pem file.
|
||||
// This is used to detect changes in the secret that contains the certificates
|
||||
// This is used to detect changes in the secret that contains certificates
|
||||
PemSHA string `json:"pemSha"`
|
||||
|
||||
// CN contains all the common names defined in the SSL certificate
|
||||
CN []string `json:"cn"`
|
||||
|
||||
// ExpiresTime contains the expiration of this SSL certificate in timestamp format
|
||||
ExpireTime time.Time `json:"expires"`
|
||||
|
||||
// Pem encoded certificate and key concatenated
|
||||
PemCertKey string `json:"pemCertKey,omitempty"`
|
||||
}
|
||||
|
@ -50,5 +61,5 @@ func (s SSLCert) GetObjectKind() schema.ObjectKind {
|
|||
|
||||
// HashInclude defines if a field should be used or not to calculate the hash
|
||||
func (s SSLCert) HashInclude(field string, v interface{}) (bool, error) {
|
||||
return (field != "PemSHA" && field != "ExpireTime"), nil
|
||||
return (field != "PemSHA" && field != "CASHA" && field != "ExpireTime"), nil
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestGetObjectKindForSSLCert(t *testing.T) {
|
||||
fk := &SSLCert{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
CAFileName: "ca_file",
|
||||
PemFileName: "pemfile",
|
||||
PemSHA: "pem_sha",
|
||||
CN: []string{},
|
||||
}
|
||||
|
||||
r := fk.GetObjectKind()
|
||||
if r == nil {
|
||||
t.Errorf("Returned nil but expected a valid ObjectKind")
|
||||
}
|
||||
}
|
|
@ -179,7 +179,7 @@ type Server struct {
|
|||
// the server or in the remote endpoint
|
||||
SSLPassthrough bool `json:"sslPassthrough"`
|
||||
// SSLCert describes the certificate that will be used on the server
|
||||
SSLCert SSLCert `json:"sslCert"`
|
||||
SSLCert *SSLCert `json:"sslCert"`
|
||||
// Locations list of URIs configured in the server.
|
||||
Locations []*Location `json:"locations,omitempty"`
|
||||
// Alias return the alias of the server name
|
||||
|
@ -227,7 +227,7 @@ type Location struct {
|
|||
// Backend describes the name of the backend to use.
|
||||
Backend string `json:"backend"`
|
||||
// Service describes the referenced services from the ingress
|
||||
Service *apiv1.Service `json:"service,omitempty" hash:"ignore"`
|
||||
Service *apiv1.Service `json:"-"`
|
||||
// Port describes to which port from the service
|
||||
Port intstr.IntOrString `json:"port"`
|
||||
// Overwrite the Host header passed into the backend. Defaults to
|
||||
|
@ -290,7 +290,7 @@ type Location struct {
|
|||
ClientBodyBufferSize string `json:"clientBodyBufferSize,omitempty"`
|
||||
// DefaultBackend allows the use of a custom default backend for this location.
|
||||
// +optional
|
||||
DefaultBackend *apiv1.Service `json:"defaultBackend,omitempty" hash:"ignore"`
|
||||
DefaultBackend *apiv1.Service `json:"-"`
|
||||
// DefaultBackendUpstreamName is the upstream-formatted string for the name of
|
||||
// this location's custom default backend
|
||||
DefaultBackendUpstreamName string `json:"defaultBackendUpstreamName,omitempty"`
|
||||
|
@ -327,7 +327,7 @@ type Location struct {
|
|||
// The endpoints must provide the TLS termination exposing the required SSL certificate.
|
||||
// The ingress controller only pipes the underlying TCP connection
|
||||
type SSLPassthroughBackend struct {
|
||||
Service *apiv1.Service `json:"service,omitempty" hash:"ignore"`
|
||||
Service *apiv1.Service `json:"-"`
|
||||
Port intstr.IntOrString `json:"port"`
|
||||
// Backend describes the endpoints to use.
|
||||
Backend string `json:"namespace,omitempty"`
|
||||
|
@ -344,7 +344,7 @@ type L4Service struct {
|
|||
// Endpoints active endpoints of the service
|
||||
Endpoints []Endpoint `json:"endpoints,omitempty"`
|
||||
// k8s Service
|
||||
Service *apiv1.Service `json:"service,omitempty" hash:"ignore"`
|
||||
Service *apiv1.Service `json:"-"`
|
||||
}
|
||||
|
||||
// L4Backend describes the kubernetes service behind L4 Ingress service
|
||||
|
@ -365,7 +365,7 @@ type ProxyProtocol struct {
|
|||
|
||||
// Ingress holds the definition of an Ingress plus its annotations
|
||||
type Ingress struct {
|
||||
networking.Ingress `hash:"ignore"`
|
||||
networking.Ingress `json:"-"`
|
||||
ParsedAnnotations *annotations.Ingress `json:"parsedAnnotations"`
|
||||
}
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ func (s1 *Server) Equal(s2 *Server) bool {
|
|||
if s1.SSLPassthrough != s2.SSLPassthrough {
|
||||
return false
|
||||
}
|
||||
if !(&s1.SSLCert).Equal(&s2.SSLCert) {
|
||||
if !(s1.SSLCert).Equal(s2.SSLCert) {
|
||||
return false
|
||||
}
|
||||
if s1.Alias != s2.Alias {
|
||||
|
@ -508,7 +508,7 @@ func (s1 *SSLCert) Equal(s2 *SSLCert) bool {
|
|||
if s1 == nil || s2 == nil {
|
||||
return false
|
||||
}
|
||||
if s1.PemFileName != s2.PemFileName {
|
||||
if s1.CASHA != s2.CASHA {
|
||||
return false
|
||||
}
|
||||
if s1.PemSHA != s2.PemSHA {
|
||||
|
|
|
@ -27,8 +27,10 @@ import (
|
|||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -51,6 +53,22 @@ const (
|
|||
fakeCertificateName = "default-fake-certificate"
|
||||
)
|
||||
|
||||
func init() {
|
||||
_, err := os.Stat(file.DefaultSSLDirectory)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(file.DefaultSSLDirectory, file.ReadWriteByUser)
|
||||
if err != nil {
|
||||
klog.Fatalf("Unexpected error checking for default SSL directory: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
klog.Fatalf("Unexpected error checking for default SSL directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// getPemFileName returns absolute file path and file name of pem cert related to given fullSecretName
|
||||
func getPemFileName(fullSecretName string) (string, string) {
|
||||
pemName := fmt.Sprintf("%v.pem", fullSecretName)
|
||||
|
@ -164,88 +182,58 @@ func CreateCACert(ca []byte) (*ingress.SSLCert, error) {
|
|||
|
||||
// StoreSSLCertOnDisk creates a .pem file with content PemCertKey from the given sslCert
|
||||
// and sets relevant remaining fields of sslCert object
|
||||
func StoreSSLCertOnDisk(fs file.Filesystem, name string, sslCert *ingress.SSLCert) error {
|
||||
func StoreSSLCertOnDisk(name string, sslCert *ingress.SSLCert) (string, error) {
|
||||
pemFileName, _ := getPemFileName(name)
|
||||
|
||||
pemFile, err := fs.Create(pemFileName)
|
||||
err := ioutil.WriteFile(pemFileName, []byte(sslCert.PemCertKey), file.ReadWriteByUser)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create PEM certificate file %v: %v", pemFileName, err)
|
||||
}
|
||||
defer pemFile.Close()
|
||||
|
||||
_, err = pemFile.Write([]byte(sslCert.PemCertKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write data to PEM file %v: %v", pemFileName, err)
|
||||
return "", fmt.Errorf("could not create PEM certificate file %v: %v", pemFileName, err)
|
||||
}
|
||||
|
||||
sslCert.PemFileName = pemFileName
|
||||
sslCert.PemSHA = file.SHA1(pemFileName)
|
||||
|
||||
return nil
|
||||
return pemFileName, nil
|
||||
}
|
||||
|
||||
// ConfigureCACertWithCertAndKey appends ca into existing PEM file consisting of cert and key
|
||||
// and sets relevant fields in sslCert object
|
||||
func ConfigureCACertWithCertAndKey(fs file.Filesystem, name string, ca []byte, sslCert *ingress.SSLCert) error {
|
||||
func ConfigureCACertWithCertAndKey(name string, ca []byte, sslCert *ingress.SSLCert) error {
|
||||
err := verifyPemCertAgainstRootCA(sslCert.Certificate, ca)
|
||||
if err != nil {
|
||||
oe := fmt.Sprintf("failed to verify certificate chain: \n\t%s\n", err)
|
||||
return errors.New(oe)
|
||||
}
|
||||
|
||||
certAndKey, err := fs.ReadFile(sslCert.PemFileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read file %v for writing additional CA chains: %v", sslCert.PemFileName, err)
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
|
||||
f, err := fs.Create(sslCert.PemFileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create PEM file %v: %v", sslCert.PemFileName, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(certAndKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write cert and key bundle to cert file %v: %v", sslCert.PemFileName, err)
|
||||
}
|
||||
|
||||
_, err = f.Write([]byte("\n"))
|
||||
_, err = buffer.Write([]byte(sslCert.PemCertKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.PemFileName, err)
|
||||
}
|
||||
|
||||
_, err = f.Write(ca)
|
||||
_, err = buffer.Write([]byte("\n"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.PemFileName, err)
|
||||
}
|
||||
|
||||
_, err = buffer.Write(ca)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write ca data to cert file %v: %v", sslCert.PemFileName, err)
|
||||
}
|
||||
|
||||
sslCert.CAFileName = sslCert.PemFileName
|
||||
// since we updated sslCert.PemFileName we need to recalculate the checksum
|
||||
sslCert.PemSHA = file.SHA1(sslCert.PemFileName)
|
||||
|
||||
return nil
|
||||
return ioutil.WriteFile(sslCert.CAFileName, buffer.Bytes(), 0644)
|
||||
}
|
||||
|
||||
// ConfigureCACert is similar to ConfigureCACertWithCertAndKey but it creates a separate file
|
||||
// for CA cert and writes only ca into it and then sets relevant fields in sslCert
|
||||
func ConfigureCACert(fs file.Filesystem, name string, ca []byte, sslCert *ingress.SSLCert) error {
|
||||
func ConfigureCACert(name string, ca []byte, sslCert *ingress.SSLCert) error {
|
||||
caName := fmt.Sprintf("ca-%v.pem", name)
|
||||
fileName := fmt.Sprintf("%v/%v", file.DefaultSSLDirectory, caName)
|
||||
|
||||
f, err := fs.Create(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write CA file %v: %v", fileName, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(ca)
|
||||
err := ioutil.WriteFile(fileName, ca, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write CA file %v: %v", fileName, err)
|
||||
}
|
||||
|
||||
sslCert.PemFileName = fileName
|
||||
sslCert.CAFileName = fileName
|
||||
sslCert.PemSHA = file.SHA1(fileName)
|
||||
|
||||
klog.V(3).Infof("Created CA Certificate for Authentication: %v", fileName)
|
||||
|
||||
|
@ -319,10 +307,10 @@ func parseSANExtension(value []byte) (dnsNames, emailAddresses []string, ipAddre
|
|||
}
|
||||
|
||||
// AddOrUpdateDHParam creates a dh parameters file with the specified name
|
||||
func AddOrUpdateDHParam(name string, dh []byte, fs file.Filesystem) (string, error) {
|
||||
func AddOrUpdateDHParam(name string, dh []byte) (string, error) {
|
||||
pemFileName, pemName := getPemFileName(name)
|
||||
|
||||
tempPemFile, err := fs.TempFile(file.DefaultSSLDirectory, pemName)
|
||||
tempPemFile, err := ioutil.TempFile(file.DefaultSSLDirectory, pemName)
|
||||
|
||||
klog.V(3).Infof("Creating temp file %v for DH param: %v", tempPemFile.Name(), pemName)
|
||||
if err != nil {
|
||||
|
@ -339,9 +327,9 @@ func AddOrUpdateDHParam(name string, dh []byte, fs file.Filesystem) (string, err
|
|||
return "", fmt.Errorf("could not close temp pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
|
||||
defer fs.RemoveAll(tempPemFile.Name())
|
||||
defer os.Remove(tempPemFile.Name())
|
||||
|
||||
pemCerts, err := fs.ReadFile(tempPemFile.Name())
|
||||
pemCerts, err := ioutil.ReadFile(tempPemFile.Name())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -356,7 +344,7 @@ func AddOrUpdateDHParam(name string, dh []byte, fs file.Filesystem) (string, err
|
|||
return "", fmt.Errorf("certificate %v contains invalid data", name)
|
||||
}
|
||||
|
||||
err = fs.Rename(tempPemFile.Name(), pemFileName)
|
||||
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)
|
||||
}
|
||||
|
@ -366,7 +354,7 @@ func AddOrUpdateDHParam(name string, dh []byte, fs file.Filesystem) (string, err
|
|||
|
||||
// GetFakeSSLCert creates a Self Signed Certificate
|
||||
// Based in the code https://golang.org/src/crypto/tls/generate_cert.go
|
||||
func GetFakeSSLCert(fs file.Filesystem) *ingress.SSLCert {
|
||||
func GetFakeSSLCert() *ingress.SSLCert {
|
||||
cert, key := getFakeHostSSLCert("ingress.local")
|
||||
|
||||
sslCert, err := CreateSSLCert(cert, key)
|
||||
|
@ -374,11 +362,14 @@ func GetFakeSSLCert(fs file.Filesystem) *ingress.SSLCert {
|
|||
klog.Fatalf("unexpected error creating fake SSL Cert: %v", err)
|
||||
}
|
||||
|
||||
err = StoreSSLCertOnDisk(fs, fakeCertificateName, sslCert)
|
||||
path, err := StoreSSLCertOnDisk(fakeCertificateName, sslCert)
|
||||
if err != nil {
|
||||
klog.Fatalf("unexpected error storing fake SSL Cert: %v", err)
|
||||
}
|
||||
|
||||
sslCert.PemFileName = path
|
||||
sslCert.PemSHA = file.SHA1(path)
|
||||
|
||||
return sslCert
|
||||
}
|
||||
|
||||
|
@ -478,7 +469,6 @@ func IsValidHostname(hostname string, commonNames []string) bool {
|
|||
type TLSListener struct {
|
||||
certificatePath string
|
||||
keyPath string
|
||||
fs file.Filesystem
|
||||
certificate *tls.Certificate
|
||||
err error
|
||||
lock sync.Mutex
|
||||
|
@ -487,14 +477,9 @@ type TLSListener struct {
|
|||
// NewTLSListener watches changes to th certificate and key paths
|
||||
// and reloads it whenever it changes
|
||||
func NewTLSListener(certificate, key string) *TLSListener {
|
||||
fs, err := file.NewLocalFS()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to instanciate certificate: %v", err))
|
||||
}
|
||||
l := TLSListener{
|
||||
certificatePath: certificate,
|
||||
keyPath: key,
|
||||
fs: fs,
|
||||
lock: sync.Mutex{},
|
||||
}
|
||||
l.load()
|
||||
|
@ -519,12 +504,12 @@ func (tl *TLSListener) TLSConfig() *tls.Config {
|
|||
|
||||
func (tl *TLSListener) load() {
|
||||
klog.Infof("loading tls certificate from certificate path %s and key path %s", tl.certificatePath, tl.keyPath)
|
||||
certBytes, err := tl.fs.ReadFile(tl.certificatePath)
|
||||
certBytes, err := ioutil.ReadFile(tl.certificatePath)
|
||||
if err != nil {
|
||||
tl.certificate = nil
|
||||
tl.err = err
|
||||
}
|
||||
keyBytes, err := tl.fs.ReadFile(tl.keyPath)
|
||||
keyBytes, err := ioutil.ReadFile(tl.keyPath)
|
||||
if err != nil {
|
||||
tl.certificate = nil
|
||||
tl.err = err
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/big"
|
||||
"net/http"
|
||||
|
@ -38,9 +39,6 @@ import (
|
|||
"time"
|
||||
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubernetes/pkg/util/filesystem"
|
||||
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
)
|
||||
|
||||
// generateRSACerts generates a self signed certificate using a self generated ca
|
||||
|
@ -71,8 +69,6 @@ func generateRSACerts(host string) (*keyPair, *keyPair, error) {
|
|||
}
|
||||
|
||||
func TestStoreSSLCertOnDisk(t *testing.T) {
|
||||
fs := newFS(t)
|
||||
|
||||
cert, _, err := generateRSACerts("echoheaders")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
|
@ -88,13 +84,13 @@ func TestStoreSSLCertOnDisk(t *testing.T) {
|
|||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
err = StoreSSLCertOnDisk(fs, name, sslCert)
|
||||
_, err = StoreSSLCertOnDisk(name, sslCert)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error storing SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
if sslCert.PemFileName == "" {
|
||||
t.Fatalf("expected path to pem file but returned empty")
|
||||
if sslCert.PemCertKey == "" {
|
||||
t.Fatalf("expected a pem certificate returned empty")
|
||||
}
|
||||
|
||||
if len(sslCert.CN) == 0 {
|
||||
|
@ -107,8 +103,6 @@ func TestStoreSSLCertOnDisk(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCACert(t *testing.T) {
|
||||
fs := newFS(t)
|
||||
|
||||
cert, CA, err := generateRSACerts("echoheaders")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
|
@ -125,16 +119,14 @@ func TestCACert(t *testing.T) {
|
|||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
err = StoreSSLCertOnDisk(fs, name, sslCert)
|
||||
path, err := StoreSSLCertOnDisk(name, sslCert)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error storing SSL certificate: %v", err)
|
||||
}
|
||||
|
||||
if sslCert.CAFileName != "" {
|
||||
t.Fatalf("expected CA file name to be empty")
|
||||
}
|
||||
sslCert.CAFileName = path
|
||||
|
||||
err = ConfigureCACertWithCertAndKey(fs, name, ca, sslCert)
|
||||
err = ConfigureCACertWithCertAndKey(name, ca, sslCert)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error configuring CA certificate: %v", err)
|
||||
}
|
||||
|
@ -145,9 +137,7 @@ func TestCACert(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetFakeSSLCert(t *testing.T) {
|
||||
fs := newFS(t)
|
||||
|
||||
sslCert := GetFakeSSLCert(fs)
|
||||
sslCert := GetFakeSSLCert()
|
||||
|
||||
if len(sslCert.PemCertKey) == 0 {
|
||||
t.Fatalf("expected PemCertKey to not be empty")
|
||||
|
@ -171,8 +161,6 @@ func TestGetFakeSSLCert(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestConfigureCACert(t *testing.T) {
|
||||
fs := newFS(t)
|
||||
|
||||
cn := "demo-ca"
|
||||
_, ca, err := generateRSACerts(cn)
|
||||
if err != nil {
|
||||
|
@ -191,7 +179,7 @@ func TestConfigureCACert(t *testing.T) {
|
|||
t.Fatalf("expected Certificate to be set")
|
||||
}
|
||||
|
||||
err = ConfigureCACert(fs, cn, c, sslCert)
|
||||
err = ConfigureCACert(cn, c, sslCert)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating SSL certificate: %v", err)
|
||||
}
|
||||
|
@ -200,14 +188,6 @@ func TestConfigureCACert(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func newFS(t *testing.T) file.Filesystem {
|
||||
fs, err := file.NewFakeFS()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating filesystem: %v", err)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
func TestCreateSSLCert(t *testing.T) {
|
||||
cert, _, err := generateRSACerts("echoheaders")
|
||||
if err != nil {
|
||||
|
@ -360,19 +340,26 @@ func encodeCertPEM(cert *x509.Certificate) []byte {
|
|||
return pem.EncodeToMemory(&block)
|
||||
}
|
||||
|
||||
func fakeCertificate(t *testing.T, fs filesystem.Filesystem) []byte {
|
||||
func newFakeCertificate(t *testing.T) ([]byte, string, string) {
|
||||
cert, key := getFakeHostSSLCert("localhost")
|
||||
fd, err := fs.Create("/key.crt")
|
||||
|
||||
certFile, err := ioutil.TempFile("", "crt-")
|
||||
if err != nil {
|
||||
t.Errorf("failed to write test key: %v", err)
|
||||
}
|
||||
fd.Write(cert)
|
||||
fd, err = fs.Create("/key.key")
|
||||
|
||||
certFile.Write(cert)
|
||||
defer certFile.Close()
|
||||
|
||||
keyFile, err := ioutil.TempFile("", "key-")
|
||||
if err != nil {
|
||||
t.Errorf("failed to write test key: %v", err)
|
||||
}
|
||||
fd.Write(key)
|
||||
return cert
|
||||
|
||||
keyFile.Write(key)
|
||||
defer keyFile.Close()
|
||||
|
||||
return cert, certFile.Name(), keyFile.Name()
|
||||
}
|
||||
|
||||
func dialTestServer(port string, rootCertificates ...[]byte) error {
|
||||
|
@ -386,6 +373,7 @@ func dialTestServer(port string, rootCertificates ...[]byte) error {
|
|||
resp, err := tls.Dial("tcp", "localhost:"+port, &tls.Config{
|
||||
RootCAs: roots,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -396,13 +384,11 @@ func dialTestServer(port string, rootCertificates ...[]byte) error {
|
|||
}
|
||||
|
||||
func TestTLSKeyReloader(t *testing.T) {
|
||||
fs := filesystem.NewFakeFs()
|
||||
cert := fakeCertificate(t, fs)
|
||||
cert, certFile, keyFile := newFakeCertificate(t)
|
||||
|
||||
watcher := TLSListener{
|
||||
certificatePath: "/key.crt",
|
||||
keyPath: "/key.key",
|
||||
fs: fs,
|
||||
certificatePath: certFile,
|
||||
keyPath: keyFile,
|
||||
lock: sync.Mutex{},
|
||||
}
|
||||
watcher.load()
|
||||
|
@ -427,19 +413,22 @@ func TestTLSKeyReloader(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("with a new certificate", func(t *testing.T) {
|
||||
newCert := fakeCertificate(t, fs)
|
||||
cert, certFile, keyFile = newFakeCertificate(t)
|
||||
t.Run("when the certificate is not reloaded", func(t *testing.T) {
|
||||
if dialTestServer(port, newCert) == nil {
|
||||
if dialTestServer(port, cert) == nil {
|
||||
t.Errorf("TLS dial should fail")
|
||||
}
|
||||
})
|
||||
|
||||
//TODO: fix
|
||||
/*
|
||||
// simulate watch.NewFileWatcher to call the load function
|
||||
watcher.load()
|
||||
t.Run("when the certificate is reloaded", func(t *testing.T) {
|
||||
if err := dialTestServer(port, newCert); err != nil {
|
||||
if err := dialTestServer(port, cert); err != nil {
|
||||
t.Errorf("TLS dial should succeed, got error: %v", err)
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ import (
|
|||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// TemplatePath path of the NGINX template
|
||||
var TemplatePath = "/etc/nginx/template/nginx.tmpl"
|
||||
|
||||
// PID defines the location of the pid file used by NGINX
|
||||
var PID = "/tmp/nginx.pid"
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ local function reset_backends()
|
|||
backends = {
|
||||
{
|
||||
name = "access-router-production-web-80", port = "80", secure = false,
|
||||
secureCACert = { secret = "", caFilename = "", pemSha = "" },
|
||||
secureCACert = { secret = "", caFilename = "", caSha = "" },
|
||||
sslPassthrough = false,
|
||||
endpoints = {
|
||||
{ address = "10.184.7.40", port = "8080", maxFails = 0, failTimeout = 0 },
|
||||
|
|
|
@ -96,14 +96,12 @@ http {
|
|||
end
|
||||
{{ end }}
|
||||
|
||||
{{ if $all.EnableDynamicCertificates }}
|
||||
ok, res = pcall(require, "certificate")
|
||||
if not ok then
|
||||
error("require failed: " .. tostring(res))
|
||||
else
|
||||
certificate = res
|
||||
end
|
||||
{{ end }}
|
||||
|
||||
ok, res = pcall(require, "plugins")
|
||||
if not ok then
|
||||
|
@ -383,6 +381,10 @@ http {
|
|||
|
||||
ssl_ecdh_curve {{ $cfg.SSLECDHCurve }};
|
||||
|
||||
# PEM sha: {{ $cfg.DefaultSSLCertificate.PemSHA }}
|
||||
ssl_certificate {{ $cfg.DefaultSSLCertificate.PemFileName }};
|
||||
ssl_certificate_key {{ $cfg.DefaultSSLCertificate.PemFileName }};
|
||||
|
||||
{{ if gt (len $cfg.CustomHTTPErrors) 0 }}
|
||||
proxy_intercept_errors on;
|
||||
{{ end }}
|
||||
|
@ -474,18 +476,9 @@ http {
|
|||
{{ buildHTTPListener $all $redirect.From }}
|
||||
{{ buildHTTPSListener $all $redirect.From }}
|
||||
|
||||
{{ if not (empty $redirect.SSLCert.PemFileName) }}
|
||||
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
|
||||
# PEM sha: {{ $redirect.SSLCert.PemSHA }}
|
||||
ssl_certificate {{ $redirect.SSLCert.PemFileName }};
|
||||
ssl_certificate_key {{ $redirect.SSLCert.PemFileName }};
|
||||
|
||||
{{ if $all.EnableDynamicCertificates}}
|
||||
ssl_certificate_by_lua_block {
|
||||
certificate.call()
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if gt (len $cfg.BlockUserAgents) 0 }}
|
||||
if ($block_ua) {
|
||||
|
@ -748,7 +741,7 @@ stream {
|
|||
proxy_set_header X-Request-ID $req_id;
|
||||
proxy_set_header Host $best_http_host;
|
||||
|
||||
set $proxy_upstream_name {{ $upstreamName }};
|
||||
set $proxy_upstream_name {{ $upstreamName | quote }};
|
||||
|
||||
rewrite (.*) / break;
|
||||
|
||||
|
@ -794,18 +787,9 @@ stream {
|
|||
|
||||
set $proxy_upstream_name "-";
|
||||
|
||||
{{ if not (empty $server.SSLCert.PemFileName) }}
|
||||
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
|
||||
# PEM sha: {{ $server.SSLCert.PemSHA }}
|
||||
ssl_certificate {{ $server.SSLCert.PemFileName }};
|
||||
ssl_certificate_key {{ $server.SSLCert.PemFileName }};
|
||||
|
||||
{{ if $all.EnableDynamicCertificates}}
|
||||
ssl_certificate_by_lua_block {
|
||||
certificate.call()
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty $server.AuthTLSError) }}
|
||||
# {{ $server.AuthTLSError }}
|
||||
|
@ -813,7 +797,7 @@ stream {
|
|||
{{ else }}
|
||||
|
||||
{{ if not (empty $server.CertificateAuth.CAFileName) }}
|
||||
# PEM sha: {{ $server.CertificateAuth.PemSHA }}
|
||||
# PEM sha: {{ $server.CertificateAuth.CASHA }}
|
||||
ssl_client_certificate {{ $server.CertificateAuth.CAFileName }};
|
||||
ssl_verify_client {{ $server.CertificateAuth.VerifyClient }};
|
||||
ssl_verify_depth {{ $server.CertificateAuth.ValidationDepth }};
|
||||
|
@ -1043,7 +1027,7 @@ stream {
|
|||
plugins.run()
|
||||
}
|
||||
|
||||
{{ if (and (not (empty $server.SSLCert.PemFileName)) $all.Cfg.HSTS) }}
|
||||
{{ if (and $server.SSLCert $all.Cfg.HSTS) }}
|
||||
if ($scheme = https) {
|
||||
more_set_headers "Strict-Transport-Security: max-age={{ $all.Cfg.HSTSMaxAge }}{{ if $all.Cfg.HSTSIncludeSubdomains }}; includeSubDomains{{ end }}{{ if $all.Cfg.HSTSPreload }}; preload{{ end }}";
|
||||
}
|
||||
|
|
|
@ -168,17 +168,13 @@ var _ = framework.IngressNginxDescribe("Annotations - AuthTLS", func() {
|
|||
})
|
||||
|
||||
func assertSslClientCertificateConfig(f *framework.Framework, host string, verifyClient string, verifyDepth string) {
|
||||
sslCertDirective := "ssl_certificate /etc/ingress-controller/ssl/default-fake-certificate.pem;"
|
||||
sslKeyDirective := "ssl_certificate_key /etc/ingress-controller/ssl/default-fake-certificate.pem;"
|
||||
sslClientCertDirective := fmt.Sprintf("ssl_client_certificate /etc/ingress-controller/ssl/%s-%s.pem;", f.Namespace, host)
|
||||
sslVerify := fmt.Sprintf("ssl_verify_client %s;", verifyClient)
|
||||
sslVerifyDepth := fmt.Sprintf("ssl_verify_depth %s;", verifyDepth)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, sslCertDirective) &&
|
||||
strings.Contains(server, sslKeyDirective) &&
|
||||
strings.Contains(server, sslClientCertDirective) &&
|
||||
return strings.Contains(server, sslClientCertDirective) &&
|
||||
strings.Contains(server, sslVerify) &&
|
||||
strings.Contains(server, sslVerifyDepth)
|
||||
})
|
||||
|
|
|
@ -165,6 +165,23 @@ func (f *Framework) GetNginxIP() string {
|
|||
return s.Spec.ClusterIP
|
||||
}
|
||||
|
||||
// GetNginxPodIP returns the IP addres/es of the running pods
|
||||
func (f *Framework) GetNginxPodIP() []string {
|
||||
e, err := f.KubeClientSet.
|
||||
CoreV1().
|
||||
Endpoints(f.Namespace).
|
||||
Get("ingress-nginx", metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "unexpected error obtaning NGINX IP address")
|
||||
eips := make([]string, 0)
|
||||
for _, s := range e.Subsets {
|
||||
for _, a := range s.Addresses {
|
||||
eips = append(eips, a.IP)
|
||||
}
|
||||
}
|
||||
|
||||
return eips
|
||||
}
|
||||
|
||||
// GetURL returns the URL should be used to make a request to NGINX
|
||||
func (f *Framework) GetURL(scheme RequestScheme) string {
|
||||
ip := f.GetNginxIP()
|
||||
|
|
|
@ -18,6 +18,7 @@ package framework
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
@ -31,7 +32,6 @@ import (
|
|||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/ingress-nginx/internal/file"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -205,13 +205,13 @@ func secretInNamespace(c kubernetes.Interface, namespace, name string) wait.Cond
|
|||
}
|
||||
|
||||
// WaitForFileInFS waits a default amount of time for the specified file is present in the filesystem
|
||||
func WaitForFileInFS(file string, fs file.Filesystem) error {
|
||||
return wait.PollImmediate(Poll, DefaultTimeout, fileInFS(file, fs))
|
||||
func WaitForFileInFS(file string) error {
|
||||
return wait.PollImmediate(Poll, DefaultTimeout, fileInFS(file))
|
||||
}
|
||||
|
||||
func fileInFS(file string, fs file.Filesystem) wait.ConditionFunc {
|
||||
func fileInFS(file string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
stat, err := fs.Stat(file)
|
||||
stat, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ import (
|
|||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
"github.com/prometheus/common/model"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
|
@ -67,23 +70,22 @@ var _ = framework.IngressNginxDescribe("Dynamic Certificate", func() {
|
|||
|
||||
time.Sleep(waitForLuaSync)
|
||||
|
||||
ip := f.GetNginxPodIP()
|
||||
mf, err := f.GetMetric("nginx_ingress_controller_success", ip[0])
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mf).ToNot(BeNil())
|
||||
|
||||
rc0, err := extractReloadCount(mf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ensureHTTPSRequest(fmt.Sprintf("%s?id=dummy_log_splitter_foo_bar", f.GetURL(framework.HTTPS)), host, "ingress.local")
|
||||
|
||||
_, err := framework.CreateIngressTLSSecret(f.KubeClientSet,
|
||||
_, err = framework.CreateIngressTLSSecret(f.KubeClientSet,
|
||||
ing.Spec.TLS[0].Hosts,
|
||||
ing.Spec.TLS[0].SecretName,
|
||||
ing.Namespace)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
By("configuring certificate_by_lua and skipping Nginx configuration of the new certificate")
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "ssl_certificate_by_lua_block") &&
|
||||
!strings.Contains(server, fmt.Sprintf("ssl_certificate /etc/ingress-controller/ssl/%s-%s.pem;", ing.Namespace, host)) &&
|
||||
!strings.Contains(server, fmt.Sprintf("ssl_certificate_key /etc/ingress-controller/ssl/%s-%s.pem;", ing.Namespace, host)) &&
|
||||
strings.Contains(server, "listen 443")
|
||||
})
|
||||
|
||||
time.Sleep(waitForLuaSync)
|
||||
|
||||
By("serving the configured certificate on HTTPS endpoint")
|
||||
|
@ -92,12 +94,17 @@ var _ = framework.IngressNginxDescribe("Dynamic Certificate", func() {
|
|||
log, err := f.NginxLogs()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(log).ToNot(BeEmpty())
|
||||
index := strings.Index(log, "id=dummy_log_splitter_foo_bar")
|
||||
restOfLogs := log[index:]
|
||||
|
||||
By("skipping Nginx reload")
|
||||
Expect(restOfLogs).ToNot(ContainSubstring(logRequireBackendReload))
|
||||
Expect(restOfLogs).ToNot(ContainSubstring(logBackendReloadSuccess))
|
||||
mf, err = f.GetMetric("nginx_ingress_controller_success", ip[0])
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mf).ToNot(BeNil())
|
||||
|
||||
rc1, err := extractReloadCount(mf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// TODO: This is wrong. We should not require a reload when SSL is configured
|
||||
Expect(rc0).To(BeEquivalentTo(rc1 - 1))
|
||||
})
|
||||
|
||||
Context("given an ingress with TLS correctly configured", func() {
|
||||
|
@ -118,10 +125,7 @@ var _ = framework.IngressNginxDescribe("Dynamic Certificate", func() {
|
|||
By("configuring certificate_by_lua and skipping Nginx configuration of the new certificate")
|
||||
f.WaitForNginxServer(ing.Spec.TLS[0].Hosts[0],
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "ssl_certificate_by_lua_block") &&
|
||||
!strings.Contains(server, fmt.Sprintf("ssl_certificate /etc/ingress-controller/ssl/%s-%s.pem;", ing.Namespace, host)) &&
|
||||
!strings.Contains(server, fmt.Sprintf("ssl_certificate_key /etc/ingress-controller/ssl/%s-%s.pem;", ing.Namespace, host)) &&
|
||||
strings.Contains(server, "listen 443")
|
||||
return strings.Contains(server, "listen 443")
|
||||
})
|
||||
|
||||
time.Sleep(waitForLuaSync)
|
||||
|
@ -150,19 +154,15 @@ var _ = framework.IngressNginxDescribe("Dynamic Certificate", func() {
|
|||
ing.Spec.TLS[0].SecretName,
|
||||
ing.Namespace)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
time.Sleep(waitForLuaSync)
|
||||
|
||||
By("configuring certificate_by_lua and skipping Nginx configuration of the new certificate")
|
||||
f.WaitForNginxServer(ing.Spec.TLS[0].Hosts[0],
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "ssl_certificate_by_lua_block") &&
|
||||
!strings.Contains(server, fmt.Sprintf("ssl_certificate /etc/ingress-controller/ssl/%s-%s.pem;", ing.Namespace, host)) &&
|
||||
!strings.Contains(server, fmt.Sprintf("ssl_certificate_key /etc/ingress-controller/ssl/%s-%s.pem;", ing.Namespace, host)) &&
|
||||
strings.Contains(server, "listen 443")
|
||||
return strings.Contains(server, "listen 443")
|
||||
})
|
||||
|
||||
time.Sleep(waitForLuaSync)
|
||||
|
||||
By("serving the configured certificate on HTTPS endpoint")
|
||||
ensureHTTPSRequest(f.GetURL(framework.HTTPS), host, host)
|
||||
|
||||
|
@ -177,39 +177,41 @@ var _ = framework.IngressNginxDescribe("Dynamic Certificate", func() {
|
|||
Expect(restOfLogs).ToNot(ContainSubstring(logBackendReloadSuccess))
|
||||
})
|
||||
|
||||
// TODO: Fix
|
||||
/*
|
||||
It("falls back to using default certificate when secret gets deleted without reloading", func() {
|
||||
ing, err := f.KubeClientSet.ExtensionsV1beta1().Ingresses(f.Namespace).Get(host, metav1.GetOptions{})
|
||||
|
||||
ensureHTTPSRequest(fmt.Sprintf("%s?id=dummy_log_splitter_foo_bar", f.GetURL(framework.HTTPS)), host, host)
|
||||
|
||||
f.KubeClientSet.CoreV1().Secrets(ing.Namespace).Delete(ing.Spec.TLS[0].SecretName, nil)
|
||||
ip := f.GetNginxPodIP()
|
||||
mf, err := f.GetMetric("nginx_ingress_controller_success", ip[0])
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
time.Sleep(waitForLuaSync)
|
||||
Expect(mf).ToNot(BeNil())
|
||||
|
||||
By("configuring certificate_by_lua and skipping Nginx configuration of the new certificate")
|
||||
f.WaitForNginxServer(ing.Spec.TLS[0].Hosts[0],
|
||||
func(server string) bool {
|
||||
return strings.Contains(server, "ssl_certificate_by_lua_block") &&
|
||||
strings.Contains(server, "ssl_certificate /etc/ingress-controller/ssl/default-fake-certificate.pem;") &&
|
||||
strings.Contains(server, "ssl_certificate_key /etc/ingress-controller/ssl/default-fake-certificate.pem;") &&
|
||||
strings.Contains(server, "listen 443")
|
||||
})
|
||||
rc0, err := extractReloadCount(mf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
time.Sleep(waitForLuaSync)
|
||||
err = f.KubeClientSet.CoreV1().Secrets(ing.Namespace).Delete(ing.Spec.TLS[0].SecretName, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
time.Sleep(waitForLuaSync * 2)
|
||||
|
||||
By("serving the default certificate on HTTPS endpoint")
|
||||
ensureHTTPSRequest(f.GetURL(framework.HTTPS), host, "ingress.local")
|
||||
|
||||
log, err := f.NginxLogs()
|
||||
mf, err = f.GetMetric("nginx_ingress_controller_success", ip[0])
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mf).ToNot(BeNil())
|
||||
|
||||
rc1, err := extractReloadCount(mf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(log).ToNot(BeEmpty())
|
||||
index := strings.Index(log, "id=dummy_log_splitter_foo_bar")
|
||||
restOfLogs := log[index:]
|
||||
|
||||
By("skipping Nginx reload")
|
||||
Expect(restOfLogs).ToNot(ContainSubstring(logRequireBackendReload))
|
||||
Expect(restOfLogs).ToNot(ContainSubstring(logBackendReloadSuccess))
|
||||
// TODO: This is wrong. We should not require a reload when SSL is configured
|
||||
Expect(rc0).To(BeEquivalentTo(rc1 - 1))
|
||||
})
|
||||
*/
|
||||
|
||||
It("picks up a non-certificate only change", func() {
|
||||
newHost := "foo2.com"
|
||||
|
@ -236,3 +238,15 @@ var _ = framework.IngressNginxDescribe("Dynamic Certificate", func() {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
func extractReloadCount(mf *dto.MetricFamily) (float64, error) {
|
||||
vec, err := expfmt.ExtractSamples(&expfmt.DecodeOptions{
|
||||
Timestamp: model.Now(),
|
||||
}, mf)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return float64(vec[0].Value), nil
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ var _ = framework.IngressNginxDescribe("default-ssl-certificate", func() {
|
|||
f.EnsureIngress(ing)
|
||||
|
||||
By("making sure new ingress is deployed")
|
||||
expectedConfig := fmt.Sprintf("set $proxy_upstream_name \"%v-%v-%v\";", f.Namespace, service, port)
|
||||
expectedConfig := fmt.Sprintf(`set $proxy_upstream_name "%v-%v-%v";`, f.Namespace, service, port)
|
||||
f.WaitForNginxServer("_", func(cfg string) bool {
|
||||
return strings.Contains(cfg, expectedConfig)
|
||||
})
|
||||
|
@ -87,7 +87,7 @@ var _ = framework.IngressNginxDescribe("default-ssl-certificate", func() {
|
|||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("making sure new ingress is deployed")
|
||||
expectedConfig := fmt.Sprintf("set $proxy_upstream_name \"%v-%v-%v\";", f.Namespace, service, port)
|
||||
expectedConfig := fmt.Sprintf(`set $proxy_upstream_name "%v-%v-%v";`, f.Namespace, service, port)
|
||||
f.WaitForNginxServer(host, func(cfg string) bool {
|
||||
return strings.Contains(cfg, expectedConfig)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue