~samwhited/xmpp

cc33bc047201f1a449333cf71766ff5f1e6f6d09 — Sam Whited a month ago 565f5ca
internal/integration: simplify use of ClientCert

This patch lets us promote cmd.ClientCert to a function and use it as a
tls.Config's GetClientCertificate function.
It also requires that we write out the client certs to disk first so
that if we need to debug later we have them and don't have to guess at
what was happening.

Signed-off-by: Sam Whited <sam@samwhited.com>
1 files changed, 48 insertions(+), 41 deletions(-)

M internal/integration/integration.go
M internal/integration/integration.go => internal/integration/integration.go +48 -41
@@ 40,19 40,21 @@ import (
type Cmd struct {
	*exec.Cmd

	name        string
	cfgDir      string
	kill        context.CancelFunc
	cfgF        []func() error
	deferF      []func(*Cmd) error
	in, out     *testWriter
	c2sListener net.Listener
	s2sListener net.Listener
	c2sNetwork  string
	s2sNetwork  string
	shutdown    func(*Cmd) error
	user        jid.JID
	pass        string
	name         string
	cfgDir       string
	kill         context.CancelFunc
	cfgF         []func() error
	deferF       []func(*Cmd) error
	in, out      *testWriter
	c2sListener  net.Listener
	s2sListener  net.Listener
	c2sNetwork   string
	s2sNetwork   string
	shutdown     func(*Cmd) error
	user         jid.JID
	pass         string
	clientCrt    []byte
	clientCrtKey interface{}

	// Config is meant to be used by internal packages like prosody and ejabberd
	// to store their internal representation of the config before writing it out.


@@ 92,28 94,14 @@ func New(ctx context.Context, name string, opts ...Option) (*Cmd, error) {
	return cmd, nil
}

// ClientCert generates and returns a client certificate.
func (cmd *Cmd) ClientCert(name string) (cert tls.Certificate, err error) {
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return cert, err
	}
	crt := &x509.Certificate{
		SerialNumber: big.NewInt(1),
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(365 * 24 * time.Hour),
		DNSNames:     []string{name},
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}
	crtBytes, err := x509.CreateCertificate(rand.Reader, crt, crt, key.Public(), key)
	if err != nil {
		return cert, err
	}
	return tls.Certificate{
		Certificate: [][]byte{
			crtBytes,
		},
		PrivateKey: key,
// ClientCert returns the last configured client certificate.
// The certificate request info is currently ignored and is only there to make
// promoting this method to a function and using it as
// tls.Config.GetClientCertificate possible.
func (cmd *Cmd) ClientCert(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
	return &tls.Certificate{
		Certificate: [][]byte{cmd.clientCrt},
		PrivateKey:  cmd.clientCrtKey,
	}, nil
}



@@ 261,6 249,27 @@ func Args(f ...string) Option {

// Cert creates a private key and certificate with the given name.
func Cert(name string) Option {
	return cert(name, &x509.Certificate{
		SerialNumber: big.NewInt(1),
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(365 * 24 * time.Hour),
		DNSNames:     []string{filepath.Base(name)},
	})
}

// ClientCert creates a private key and certificate with the given name that
// can be used for TLS authentication.
func ClientCert(name string) Option {
	return cert(name, &x509.Certificate{
		SerialNumber: big.NewInt(1),
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(365 * 24 * time.Hour),
		DNSNames:     []string{filepath.Base(name)},
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	})
}

func cert(name string, crt *x509.Certificate) Option {
	return func(cmd *Cmd) error {
		key, err := rsa.GenerateKey(rand.Reader, 2048)
		if err != nil {


@@ 276,16 285,14 @@ func Cert(name string) Option {
			return err
		}
		return TempFile(name+".crt", func(_ *Cmd, w io.Writer) error {
			crt := &x509.Certificate{
				SerialNumber: big.NewInt(1),
				NotBefore:    time.Now(),
				NotAfter:     time.Now().Add(365 * 24 * time.Hour),
				DNSNames:     []string{filepath.Base(name)},
			}
			cert, err := x509.CreateCertificate(rand.Reader, crt, crt, key.Public(), key)
			if err != nil {
				return err
			}
			if len(crt.ExtKeyUsage) > 0 && crt.ExtKeyUsage[0] == x509.ExtKeyUsageClientAuth {
				cmd.clientCrt = cert
				cmd.clientCrtKey = key
			}
			return pem.Encode(w, &pem.Block{
				Type:  "CERTIFICATE",
				Bytes: cert,