consul/test/integration/consul-container/libs/cluster/encryption.go

146 lines
4.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package cluster
import (
"bytes"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/hashicorp/consul/sdk/testutil/retry"
"io"
"path/filepath"
"testing"
"github.com/hashicorp/go-uuid"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
)
const (
certVolumePrefix = "test-container-certs-"
consulUID = "100"
consulGID = "1000"
consulUserArg = consulUID + ":" + consulGID
)
func newSerfEncryptionKey() (string, error) {
key := make([]byte, 32)
n, err := rand.Reader.Read(key)
if err != nil {
return "", errors.Wrap(err, "error reading random data")
}
if n != 32 {
return "", errors.Wrap(err, "couldn't read enough entropy. Generate more entropy!")
}
return base64.StdEncoding.EncodeToString(key), nil
}
func (c *BuildContext) createTLSCAFiles(t *testing.T) {
id, err := uuid.GenerateUUID()
require.NoError(t, err, "could not create cert volume UUID")
c.certVolume = certVolumePrefix + id
// TODO: cleanup anything with the prefix?
retry.Run(t, func(r *retry.R) {
// Create a volume to hold the data.
err = utils.DockerExec([]string{"volume", "create", c.certVolume}, io.Discard)
require.NoError(r, err, "could not create docker volume to hold cert data: %s", c.certVolume)
})
t.Cleanup(func() {
_ = utils.DockerExec([]string{"volume", "rm", c.certVolume}, io.Discard)
})
retry.Run(t, func(r *retry.R) {
err := utils.DockerExec([]string{"run",
"--rm",
"-i",
"--net=none",
"-v", c.certVolume + ":/data",
"busybox:latest",
"sh", "-c",
// Need this so the permissions stick; docker seems to treat unused volumes differently.
`touch /data/VOLUME_PLACEHOLDER && chown -R ` + consulUserArg + ` /data`,
}, io.Discard)
require.NoError(r, err, "could not initialize docker volume for cert data: %s", c.certVolume)
})
retry.Run(t, func(r *retry.R) {
err = utils.DockerExec([]string{"run",
"--rm",
"-i",
"--net=none",
"-u", consulUserArg,
"-v", c.certVolume + ":/data",
"-w", "/data",
"--entrypoint", "",
c.DockerImage(),
"consul", "tls", "ca", "create",
}, io.Discard)
require.NoError(r, err, "could not create TLS certificate authority in docker volume: %s", c.certVolume)
})
var w bytes.Buffer
retry.Run(t, func(r *retry.R) {
err = utils.DockerExec([]string{"run",
"--rm",
"-i",
"--net=none",
"-u", consulUserArg,
"-v", c.certVolume + ":/data",
"-w", "/data",
"--entrypoint", "",
c.DockerImage(),
"cat", filepath.Join("/data", ConsulCACertPEM),
}, &w)
require.NoError(r, err, "could not extract TLS CA certificate authority public key from docker volume: %s", c.certVolume)
})
c.caCert = w.String()
}
func (c *BuildContext) createTLSCertFiles(t *testing.T, dc string) (keyFileName, certFileName string) {
require.NotEmpty(t, "the CA has not been initialized yet")
retry.Run(t, func(r *retry.R) {
err := utils.DockerExec([]string{"run",
"--rm",
"-i",
"--net=none",
"-u", consulUserArg,
"-v", c.certVolume + ":/data",
"-w", "/data",
"--entrypoint", "",
c.DockerImage(),
"consul", "tls", "cert", "create", "-server", "-dc", dc,
}, io.Discard)
require.NoError(r, err, "could not create TLS server certificate dc=%q in docker volume: %s", dc, c.certVolume)
})
prefix := fmt.Sprintf("%s-server-%s", dc, "consul")
certFileName = fmt.Sprintf("%s-%d.pem", prefix, c.tlsCertIndex)
keyFileName = fmt.Sprintf("%s-%d-key.pem", prefix, c.tlsCertIndex)
retry.Run(t, func(r *retry.R) {
for _, fn := range []string{certFileName, keyFileName} {
err := utils.DockerExec([]string{"run",
"--rm",
"-i",
"--net=none",
"-u", consulUserArg,
"-v", c.certVolume + ":/data:ro",
"-w", "/data",
"--entrypoint", "",
c.DockerImage(),
"stat", filepath.Join("/data", fn),
}, io.Discard)
require.NoError(r, err, "Generated TLS cert file %q does not exist in volume", fn)
}
})
return keyFileName, certFileName
}