Files
seaweedfs/weed/filer/redis_tls/redis_tls_test.go
T
Chris Lu f18ad39142 filer: honor the documented TLS options in every redis store (#10425)
The scaffold advertises enable_tls, ca_cert_path, client_cert_path and
client_key_path under redis2, redis2_sentinel and redis_cluster2, but only the
plain redis2 and redis3 stores ever read them, and under a different name,
enable_mtls. Sentinel and cluster setups quietly connected in plaintext.

Build the TLS config in one place and use it from all six stores. enable_mtls
still works. The CA and the client key pair are optional now, so enable_tls
alone verifies against the system roots, and ServerName is left unset so
go-redis validates each address the sentinel and cluster clients dial.
2026-07-24 10:26:38 -07:00

168 lines
4.6 KiB
Go

package redis_tls
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"os"
"path/filepath"
"testing"
"time"
)
type fakeConfiguration map[string]interface{}
func (c fakeConfiguration) GetString(key string) string {
value, _ := c[key].(string)
return value
}
func (c fakeConfiguration) GetBool(key string) bool {
value, _ := c[key].(bool)
return value
}
func (c fakeConfiguration) GetInt(key string) int {
value, _ := c[key].(int)
return value
}
func (c fakeConfiguration) GetStringSlice(key string) []string {
value, _ := c[key].([]string)
return value
}
func (c fakeConfiguration) SetDefault(key string, value interface{}) {
if _, found := c[key]; !found {
c[key] = value
}
}
func TestDisabled(t *testing.T) {
tlsConfig, err := Config(fakeConfiguration{"redis2.ca_cert_path": "/does/not/exist"}, "redis2.")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tlsConfig != nil {
t.Fatalf("expected no TLS config when neither enable_tls nor enable_mtls is set")
}
}
func TestEnabledWithoutCertificates(t *testing.T) {
for _, key := range []string{"enable_tls", "enable_mtls"} {
tlsConfig, err := Config(fakeConfiguration{"redis2." + key: true}, "redis2.")
if err != nil {
t.Fatalf("%s: unexpected error: %v", key, err)
}
if tlsConfig == nil {
t.Fatalf("%s: expected a TLS config", key)
}
if tlsConfig.MinVersion != tls.VersionTLS12 {
t.Errorf("%s: expected TLS 1.2 minimum, got %x", key, tlsConfig.MinVersion)
}
if tlsConfig.RootCAs != nil {
t.Errorf("%s: expected the system root pool", key)
}
if len(tlsConfig.Certificates) != 0 {
t.Errorf("%s: expected no client certificate", key)
}
if tlsConfig.ServerName != "" {
t.Errorf("%s: expected the server name to come from the dialed address, got %s", key, tlsConfig.ServerName)
}
}
}
func TestMutualTls(t *testing.T) {
dir := t.TempDir()
certPath, keyPath := writeCertificate(t, dir, "client")
tlsConfig, err := Config(fakeConfiguration{
"redis2.enable_tls": true,
"redis2.ca_cert_path": certPath,
"redis2.client_cert_path": certPath,
"redis2.client_key_path": keyPath,
}, "redis2.")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tlsConfig.RootCAs == nil {
t.Errorf("expected the CA certificate to be loaded")
}
if len(tlsConfig.Certificates) != 1 {
t.Errorf("expected the client certificate to be loaded, got %d", len(tlsConfig.Certificates))
}
}
func TestUnreadableCaCertificate(t *testing.T) {
if _, err := Config(fakeConfiguration{
"redis2.enable_tls": true,
"redis2.ca_cert_path": filepath.Join(t.TempDir(), "missing.pem"),
}, "redis2."); err == nil {
t.Fatalf("expected an error for a missing CA certificate")
}
}
func TestCaCertificateWithoutPem(t *testing.T) {
path := filepath.Join(t.TempDir(), "ca.pem")
if err := os.WriteFile(path, []byte("not a certificate"), 0600); err != nil {
t.Fatal(err)
}
if _, err := Config(fakeConfiguration{
"redis2.enable_tls": true,
"redis2.ca_cert_path": path,
}, "redis2."); err == nil {
t.Fatalf("expected an error for a CA file without any certificate")
}
}
func TestClientCertificateWithoutKey(t *testing.T) {
certPath, _ := writeCertificate(t, t.TempDir(), "client")
if _, err := Config(fakeConfiguration{
"redis2.enable_tls": true,
"redis2.client_cert_path": certPath,
}, "redis2."); err == nil {
t.Fatalf("expected an error for a client certificate without a key")
}
}
func writeCertificate(t *testing.T, dir, name string) (certPath, keyPath string) {
t.Helper()
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: name},
NotBefore: time.Unix(0, 0),
NotAfter: time.Unix(1<<31-1, 0),
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
t.Fatal(err)
}
keyBytes, err := x509.MarshalECPrivateKey(key)
if err != nil {
t.Fatal(err)
}
certPath = filepath.Join(dir, name+".crt")
keyPath = filepath.Join(dir, name+".key")
if err := os.WriteFile(certPath, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}), 0600); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(keyPath, pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}), 0600); err != nil {
t.Fatal(err)
}
return certPath, keyPath
}