From 553df8a0cc0f37aa956348f0732c748692b8da58 Mon Sep 17 00:00:00 2001 From: Antoine Cotten Date: Fri, 27 Apr 2018 14:29:08 +0200 Subject: [PATCH] Refactor e2e framework for TLS tests --- .../ingress/controller/store/store_test.go | 6 +- test/e2e/framework/ssl.go | 94 ++++++++++++++----- test/e2e/lua/dynamic_configuration.go | 2 +- test/e2e/settings/no_auth_locations.go | 2 +- test/e2e/settings/proxy_protocol.go | 2 +- test/e2e/settings/server_tokens.go | 2 +- test/e2e/ssl/secret_update.go | 2 +- 7 files changed, 80 insertions(+), 30 deletions(-) diff --git a/internal/ingress/controller/store/store_test.go b/internal/ingress/controller/store/store_test.go index 3376dfa2c..c329e03b8 100644 --- a/internal/ingress/controller/store/store_test.go +++ b/internal/ingress/controller/store/store_test.go @@ -304,7 +304,7 @@ func TestStore(t *testing.T) { storer.Run(stopCh) secretName := "not-referenced" - _, _, _, err = framework.CreateIngressTLSSecret(clientSet, []string{"foo"}, secretName, ns) + _, err = framework.CreateIngressTLSSecret(clientSet, []string{"foo"}, secretName, ns) if err != nil { t.Errorf("unexpected error creating secret: %v", err) } @@ -418,7 +418,7 @@ func TestStore(t *testing.T) { t.Errorf("unexpected error waiting for secret: %v", err) } - _, _, _, err = framework.CreateIngressTLSSecret(clientSet, []string{"foo"}, secretName, ns) + _, err = framework.CreateIngressTLSSecret(clientSet, []string{"foo"}, secretName, ns) if err != nil { t.Errorf("unexpected error creating secret: %v", err) } @@ -558,7 +558,7 @@ func TestStore(t *testing.T) { t.Errorf("expected 0 events of type Delete but %v occurred", del) } - _, _, _, err = framework.CreateIngressTLSSecret(clientSet, secretHosts, name, ns) + _, err = framework.CreateIngressTLSSecret(clientSet, secretHosts, name, ns) if err != nil { t.Errorf("unexpected error creating secret: %v", err) } diff --git a/test/e2e/framework/ssl.go b/test/e2e/framework/ssl.go index 5b9fdcf6a..f8f1a700f 100644 --- a/test/e2e/framework/ssl.go +++ b/test/e2e/framework/ssl.go @@ -20,6 +20,7 @@ import ( "bytes" "crypto/rand" "crypto/rsa" + "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -27,11 +28,13 @@ import ( "io" "math/big" "net" + net_url "net/url" "strings" "time" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" ) @@ -40,18 +43,22 @@ const ( validFor = 365 * 24 * time.Hour ) -// CreateIngressTLSSecret creates a secret containing TLS certificates for the given Ingress. -// If a secret with the same name already pathExists in the namespace of the -// Ingress, it's updated. -func CreateIngressTLSSecret(client kubernetes.Interface, hosts []string, secretName, namespace string) (host string, rootCA, privKey []byte, err error) { - var k, c bytes.Buffer - host = strings.Join(hosts, ",") - if err = generateRSACerts(host, true, &k, &c); err != nil { - return +// CreateIngressTLSSecret creates or updates a Secret containing a TLS +// certificate for the given Ingress and returns a TLS configuration suitable +// for HTTP clients to use against that particular Ingress. +func CreateIngressTLSSecret(client kubernetes.Interface, hosts []string, secretName, namespace string) (*tls.Config, error) { + if len(hosts) == 0 { + return nil, fmt.Errorf("require a non-empty host for client hello") } - cert := c.Bytes() - key := k.Bytes() - secret := &v1.Secret{ + + var k, c bytes.Buffer + host := strings.Join(hosts, ",") + if err := generateRSACert(host, true, &k, &c); err != nil { + return nil, err + } + + cert, key := c.Bytes(), k.Bytes() + newSecret := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, }, @@ -60,22 +67,31 @@ func CreateIngressTLSSecret(client kubernetes.Interface, hosts []string, secretN v1.TLSPrivateKeyKey: key, }, } - var s *v1.Secret - if s, err = client.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{}); err == nil { - s.Data = secret.Data - _, err = client.CoreV1().Secrets(namespace).Update(s) + + var apierr error + curSecret, err := client.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{}) + if err == nil && curSecret != nil { + curSecret.Data = newSecret.Data + _, apierr = client.CoreV1().Secrets(namespace).Update(curSecret) } else { - _, err = client.CoreV1().Secrets(namespace).Create(secret) + _, apierr = client.CoreV1().Secrets(namespace).Create(newSecret) } - return host, cert, key, err + if apierr != nil { + return nil, apierr + } + + serverName := hosts[0] + return tlsConfig(serverName, cert) } -// generateRSACerts generates a basic self signed certificate using a key length +// WaitForTLS waits until the TLS handshake with a given server completes successfully. +func WaitForTLS(url string, tlsConfig *tls.Config) error { + return wait.Poll(Poll, 30*time.Second, matchTLSServerName(url, tlsConfig)) +} + +// generateRSACert generates a basic self signed certificate using a key length // of rsaBits, valid for validFor time. -func generateRSACerts(host string, isCA bool, keyOut, certOut io.Writer) error { - if len(host) == 0 { - return fmt.Errorf("require a non-empty host for client hello") - } +func generateRSACert(host string, isCA bool, keyOut, certOut io.Writer) error { priv, err := rsa.GenerateKey(rand.Reader, rsaBits) if err != nil { return fmt.Errorf("failed to generate key: %v", err) @@ -129,3 +145,37 @@ func generateRSACerts(host string, isCA bool, keyOut, certOut io.Writer) error { } return nil } + +// tlsConfig returns a client TLS configuration for the given server name and +// CA certificate (PEM). +func tlsConfig(serverName string, pemCA []byte) (*tls.Config, error) { + rootCAPool := x509.NewCertPool() + if !rootCAPool.AppendCertsFromPEM(pemCA) { + return nil, fmt.Errorf("error creating CA certificate pool (%s)", serverName) + } + return &tls.Config{ + ServerName: serverName, + RootCAs: rootCAPool, + }, nil +} + +// matchTLSServerName connects to the network address corresponding to the +// given URL using the given TLS configuration and returns whether the TLS +// handshake completed successfully. +func matchTLSServerName(url string, tlsConfig *tls.Config) wait.ConditionFunc { + return func() (ready bool, err error) { + u, err := net_url.Parse(url) + if err != nil { + return + } + + conn, err := tls.Dial("tcp", u.Host, tlsConfig) + if err != nil { + return false, nil + } + conn.Close() + + ready = true + return + } +} diff --git a/test/e2e/lua/dynamic_configuration.go b/test/e2e/lua/dynamic_configuration.go index 14c4a1acd..c44f56043 100644 --- a/test/e2e/lua/dynamic_configuration.go +++ b/test/e2e/lua/dynamic_configuration.go @@ -184,7 +184,7 @@ var _ = framework.IngressNginxDescribe("Dynamic Configuration", func() { }, } - _, _, _, err = framework.CreateIngressTLSSecret(f.KubeClientSet, + _, err = framework.CreateIngressTLSSecret(f.KubeClientSet, ingress.Spec.TLS[0].Hosts, ingress.Spec.TLS[0].SecretName, ingress.Namespace) diff --git a/test/e2e/settings/no_auth_locations.go b/test/e2e/settings/no_auth_locations.go index 61779db34..a91c473f2 100644 --- a/test/e2e/settings/no_auth_locations.go +++ b/test/e2e/settings/no_auth_locations.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package setting +package settings import ( "fmt" diff --git a/test/e2e/settings/proxy_protocol.go b/test/e2e/settings/proxy_protocol.go index 645ee1b53..0201a1dbd 100644 --- a/test/e2e/settings/proxy_protocol.go +++ b/test/e2e/settings/proxy_protocol.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package setting +package settings import ( "fmt" diff --git a/test/e2e/settings/server_tokens.go b/test/e2e/settings/server_tokens.go index 3ff80196f..cbbef9f16 100644 --- a/test/e2e/settings/server_tokens.go +++ b/test/e2e/settings/server_tokens.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package setting +package settings import ( "strings" diff --git a/test/e2e/ssl/secret_update.go b/test/e2e/ssl/secret_update.go index 1f744a645..e4269730b 100644 --- a/test/e2e/ssl/secret_update.go +++ b/test/e2e/ssl/secret_update.go @@ -58,7 +58,7 @@ var _ = framework.IngressNginxDescribe("SSL", func() { Expect(err).ToNot(HaveOccurred()) Expect(ing).ToNot(BeNil()) - _, _, _, err = framework.CreateIngressTLSSecret(f.KubeClientSet, + _, err = framework.CreateIngressTLSSecret(f.KubeClientSet, ing.Spec.TLS[0].Hosts, ing.Spec.TLS[0].SecretName, ing.Namespace)