Skip to content

Commit

Permalink
add tls handshake integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jcmoraisjr committed Jun 6, 2024
1 parent 7bbf160 commit 0cc16e4
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 81 deletions.
44 changes: 38 additions & 6 deletions tests/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package framework
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"math/rand"
Expand Down Expand Up @@ -234,6 +235,12 @@ func (f *framework) Request(ctx context.Context, t *testing.T, method, host, pat
ServerName: opt.SNI,
},
}
if opt.ClientCA != nil {
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM(opt.ClientCA)
require.True(t, ok)
transport.TLSClientConfig.RootCAs = pool
}
if opt.ClientCrtPEM != nil && opt.ClientKeyPEM != nil {
cert, err := tls.X509KeyPair(opt.ClientCrtPEM, opt.ClientKeyPEM)
require.NoError(t, err)
Expand All @@ -254,15 +261,25 @@ func (f *framework) Request(ctx context.Context, t *testing.T, method, host, pat
Transport: transport,
}
var res *http.Response
if opt.ExpectResponseCode > 0 {
switch {
case opt.ExpectResponseCode > 0:
require.EventuallyWithT(t, func(collect *assert.CollectT) {
res, err = cli.Do(req)
if !assert.NoError(collect, err) {
return
}
assert.Equal(collect, opt.ExpectResponseCode, res.StatusCode)
}, 5*time.Second, time.Second)
} else {
case opt.ExpectX509Error != "":
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := cli.Do(req)
// better if matching some x509.<...>Error{} instead,
// but error.Is() does not render to true due to the server's
// x509 certificate attached to the error instance.
assert.ErrorContains(collect, err, opt.ExpectX509Error)
}, 5*time.Second, time.Second)
return Response{EchoResponse: buildEchoResponse(t, "")}
default:
res, err = cli.Do(req)
require.NoError(t, err)
}
Expand Down Expand Up @@ -329,6 +346,13 @@ metadata:
return secret
}

func (f *framework) CreateSecretTLS(ctx context.Context, t *testing.T, crt, key []byte, o ...options.Object) *corev1.Secret {
return f.CreateSecret(ctx, t, map[string][]byte{
corev1.TLSCertKey: crt,
corev1.TLSPrivateKeyKey: key,
})
}

func (f *framework) CreateService(ctx context.Context, t *testing.T, serverPort int32, o ...options.Object) *corev1.Service {
opt := options.ParseObjectOptions(o...)
data := `
Expand Down Expand Up @@ -425,14 +449,22 @@ spec:
number: 8080
`
name := randomName("ing")
hostname := name + ".local"
hostname := opt.IngressOpt.CustomHostName
if hostname == "" {
hostname = name + ".local"
}

ing := f.CreateObject(t, data).(*networking.Ingress)
ing.Name = name
ing.Spec.Rules[0].Host = hostname
ing.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name = svc.Name
opt.Apply(ing)
if opt.IngressOpt.DefaultTLS {
if opt.IngressOpt.CustomTLSSecret != "" {
ing.Spec.TLS = []networking.IngressTLS{{
Hosts: []string{hostname},
SecretName: opt.IngressOpt.CustomTLSSecret,
}}
} else if opt.IngressOpt.DefaultTLS {
ing.Spec.TLS = []networking.IngressTLS{{Hosts: []string{hostname}}}
}

Expand Down Expand Up @@ -682,7 +714,7 @@ func (f *framework) CreateObject(t *testing.T, data string) client.Object {

type EchoResponse struct {
Parsed bool
Name string
ServerName string
Port int
Path string
ReqHeaders map[string]string
Expand All @@ -700,7 +732,7 @@ func buildEchoResponse(t *testing.T, body string) EchoResponse {
require.NoError(t, err)
res := EchoResponse{
Parsed: true,
Name: header[1],
ServerName: header[1],
Port: port,
Path: header[3],
ReqHeaders: make(map[string]string),
Expand Down
27 changes: 27 additions & 0 deletions tests/framework/options/certificates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package options

type Certificate func(o *certificateOpt)

func DNS(dns ...string) Certificate {
return func(o *certificateOpt) {
o.DNS = dns
}
}

func InvalidDates() Certificate {
return func(o *certificateOpt) {
o.InvalidDates = true
}
}

type certificateOpt struct {
DNS []string
InvalidDates bool
}

func ParseCertificateOptions(opts ...Certificate) (opt certificateOpt) {
for _, o := range opts {
o(&opt)
}
return opt
}
17 changes: 15 additions & 2 deletions tests/framework/options/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,23 @@ func AddConfigKeyAnnotation(key, value string) Object {
}
}

func DefaultHostTLS() Object {
func DefaultTLS() Object {
return func(o *objectOpt) {
o.IngressOpt.DefaultTLS = true
}
}

func CustomTLS(secret string) Object {
return func(o *objectOpt) {
o.IngressOpt.CustomTLSSecret = secret
}
}

func CustomHostName(hostname string) Object {
return func(o *objectOpt) {
o.IngressOpt.CustomHostName = hostname
}
}
func Listener(name, proto string, port int32) Object {
return func(o *objectOpt) {
o.GatewayOpt.Listeners = append(o.GatewayOpt.Listeners, ListenerOpt{
Expand All @@ -45,7 +56,9 @@ type objectOpt struct {
}

type IngressOpt struct {
DefaultTLS bool
DefaultTLS bool
CustomTLSSecret string
CustomHostName string
}

type GatewayOpt struct {
Expand Down
22 changes: 18 additions & 4 deletions tests/framework/options/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,27 @@ func ExpectResponseCode(code int) Request {
}
}

func HTTPSRequest(https bool) Request {
func ExpectX509Error(msg string) Request {
return func(o *requestOpt) {
o.HTTPS = https
o.ExpectX509Error = msg
}
}

func TLSSkipVerify(skipVerify bool) Request {
func HTTPSRequest() Request {
return func(o *requestOpt) {
o.TLSSkipVerify = skipVerify
o.HTTPS = true
}
}

func TLSSkipVerify() Request {
return func(o *requestOpt) {
o.TLSSkipVerify = true
}
}

func ClientCA(ca []byte) Request {
return func(o *requestOpt) {
o.ClientCA = ca
}
}

Expand All @@ -35,8 +47,10 @@ func ClientCertificateKeyPEM(crt, key []byte) Request {

type requestOpt struct {
ExpectResponseCode int
ExpectX509Error string
HTTPS bool
TLSSkipVerify bool
ClientCA []byte
SNI string
ClientCrtPEM []byte
ClientKeyPEM []byte
Expand Down
26 changes: 18 additions & 8 deletions tests/framework/ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,27 @@ import (
"time"

"github.com/stretchr/testify/require"

"github.com/jcmoraisjr/haproxy-ingress/tests/framework/options"
)

const (
CertificateIssuerCN = "HAProxy Ingress issuer"
CertificateClientCN = "HAProxy Ingress client"
)

func CreateCA(t *testing.T, dns ...string) (ca, key []byte) {
func CreateCA(t *testing.T, cn string) (ca, key []byte) {
serial, err := rand.Int(rand.Reader, big.NewInt(2^63))
require.NoError(t, err)
notBefore := time.Now().Add(-time.Hour)
notAfter := notBefore.Add(24 * time.Hour)
template := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{
CommonName: CertificateIssuerCN,
CommonName: cn,
},
NotBefore: notBefore,
NotAfter: notAfter,
DNSNames: dns,
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 1,
Expand All @@ -45,7 +46,9 @@ func CreateCA(t *testing.T, dns ...string) (ca, key []byte) {
return ca, key
}

func CreateCertificate(t *testing.T, ca, cakey []byte, dns ...string) (crt, key []byte) {
func CreateCertificate(t *testing.T, ca, cakey []byte, cn string, o ...options.Certificate) (crt, key []byte) {
opt := options.ParseCertificateOptions(o...)

cakeyder, _ := pem.Decode(cakey)
cakeyrsa, err := x509.ParsePKCS1PrivateKey(cakeyder.Bytes)
require.NoError(t, err)
Expand All @@ -56,16 +59,23 @@ func CreateCertificate(t *testing.T, ca, cakey []byte, dns ...string) (crt, key

serial, err := rand.Int(rand.Reader, big.NewInt(2^63))
require.NoError(t, err)
notBefore := time.Now().Add(-time.Hour)
notAfter := notBefore.Add(24 * time.Hour)

var notBefore, notAfter time.Time
if opt.InvalidDates {
notBefore = time.Now().Add(-24 * time.Hour)
notAfter = notBefore.Add(12 * time.Hour)
} else {
notBefore = time.Now().Add(-time.Hour)
notAfter = notBefore.Add(24 * time.Hour)
}
template := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{
CommonName: CertificateClientCN,
CommonName: cn,
},
NotBefore: notBefore,
NotAfter: notAfter,
DNSNames: dns,
DNSNames: opt.DNS,
}
priv, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err)
Expand Down
21 changes: 21 additions & 0 deletions tests/framework/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package framework

import (
"fmt"
"math/rand"
)

func AppendStringMap(src, add map[string]string) map[string]string {
out := make(map[string]string)
for k, v := range src {
out[k] = v
}
for k, v := range add {
out[k] = v
}
return out
}

func RandomHostName() string {
return fmt.Sprintf("host-%08d.local", rand.Intn(1e8))
}
Loading

0 comments on commit 0cc16e4

Please sign in to comment.