808 lines
26 KiB
Go
808 lines
26 KiB
Go
/*
|
|
Copyright 2024 The Karmada Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package certs
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"net"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
certutil "k8s.io/client-go/util/cert"
|
|
"k8s.io/client-go/util/keyutil"
|
|
|
|
"github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1"
|
|
"github.com/karmada-io/karmada/operator/pkg/constants"
|
|
"github.com/karmada-io/karmada/operator/pkg/util"
|
|
)
|
|
|
|
var (
|
|
expectedAPIServerAltDNSNames = []string{
|
|
"localhost",
|
|
"kubernetes",
|
|
"kubernetes.default",
|
|
"kubernetes.default.svc",
|
|
fmt.Sprintf("*.%s.svc.cluster.local", constants.KarmadaSystemNamespace),
|
|
fmt.Sprintf("*.%s.svc", constants.KarmadaSystemNamespace),
|
|
}
|
|
expectedAPIServerAltIPs = []net.IP{net.IPv4(127, 0, 0, 1)}
|
|
)
|
|
|
|
func TestCertConfig_defaultPublicKeyAlgorithm(t *testing.T) {
|
|
c := &CertConfig{}
|
|
c.defaultPublicKeyAlgorithm()
|
|
|
|
if c.PublicKeyAlgorithm != x509.RSA {
|
|
t.Errorf("expected PublicKeyAlgorithm to be RSA, got %v", c.PublicKeyAlgorithm)
|
|
}
|
|
}
|
|
|
|
func TestCertConfig_defaultNotAfter(t *testing.T) {
|
|
c := &CertConfig{}
|
|
c.defaultNotAfter()
|
|
|
|
if c.NotAfter == nil {
|
|
t.Error("expected NotAfter to be set, but it was nil")
|
|
}
|
|
|
|
if !c.NotAfter.After(time.Now()) {
|
|
t.Errorf("expected NotAfter to be a future time, got %v", c.NotAfter)
|
|
}
|
|
|
|
expectedTime := time.Now().Add(constants.CertificateValidity).UTC()
|
|
if c.NotAfter.Sub(expectedTime) > time.Minute {
|
|
t.Errorf("NotAfter time is too far from expected, got %v, expected %v", c.NotAfter, expectedTime)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertRootCA(t *testing.T) {
|
|
certConfig := KarmadaCertRootCA()
|
|
|
|
expectedCommonName := "karmada"
|
|
|
|
if certConfig.Name != constants.CaCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.CaCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertAdmin(t *testing.T) {
|
|
certConfig := KarmadaCertAdmin()
|
|
|
|
err := certConfig.AltNamesMutatorFunc(&AltNamesMutatorConfig{}, certConfig)
|
|
if err != nil {
|
|
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
|
}
|
|
|
|
expectedCommonName := "system:admin"
|
|
expectedOrganization := []string{"system:masters"}
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
|
|
|
if certConfig.Name != constants.KarmadaCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.KarmadaCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.CAName != constants.CaCertAndKeyName {
|
|
t.Errorf("expected CAName to be %s, got %s", constants.CaCertAndKeyName, certConfig.CAName)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Organization, expectedOrganization) {
|
|
t.Errorf("expected Organization to contain %v, got %v", expectedOrganization, certConfig.Config.Organization)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth and ExtKeyUsageClientAuth for mutual TLS, got %v", certConfig.Config.Usages)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedAPIServerAltDNSNames) {
|
|
t.Errorf("expected DNSNames to contain %v, got %v", expectedAPIServerAltDNSNames, certConfig.Config.AltNames.DNSNames)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedAPIServerAltIPs) {
|
|
t.Errorf("expected IPs to contain %v, got %v", expectedAPIServerAltIPs, certConfig.Config.AltNames.IPs)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertApiserver(t *testing.T) {
|
|
certConfig := KarmadaCertApiserver()
|
|
|
|
err := certConfig.AltNamesMutatorFunc(&AltNamesMutatorConfig{}, certConfig)
|
|
if err != nil {
|
|
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
|
}
|
|
|
|
expectedCommonName := "karmada-apiserver"
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
|
|
|
if certConfig.Name != constants.ApiserverCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.ApiserverCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.CAName != constants.CaCertAndKeyName {
|
|
t.Errorf("expected CAName to be %s, got %s", constants.CaCertAndKeyName, certConfig.CAName)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth, got %v", certConfig.Config.Usages)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedAPIServerAltDNSNames) {
|
|
t.Errorf("expected DNSNames to contain %v, got %v", expectedAPIServerAltDNSNames, certConfig.Config.AltNames.DNSNames)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedAPIServerAltIPs) {
|
|
t.Errorf("expected IPs to contain %v, got %v", expectedAPIServerAltIPs, certConfig.Config.AltNames.IPs)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertClient(t *testing.T) {
|
|
newControlPlaneAddress := "192.168.1.101"
|
|
newCertSAN := "10.0.0.15"
|
|
|
|
certConfig := KarmadaCertClient()
|
|
|
|
err := certConfig.AltNamesMutatorFunc(&AltNamesMutatorConfig{
|
|
ControlplaneAddress: newControlPlaneAddress,
|
|
Components: &v1alpha1.KarmadaComponents{
|
|
KarmadaAPIServer: &v1alpha1.KarmadaAPIServer{
|
|
CertSANs: []string{newCertSAN},
|
|
},
|
|
},
|
|
}, certConfig)
|
|
if err != nil {
|
|
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
|
}
|
|
|
|
expectedCertName := "karmada-client"
|
|
expectedCommonName := "system:admin"
|
|
expectedOrganization := []string{"system:masters"}
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
|
|
|
newIPs := []net.IP{net.ParseIP(newControlPlaneAddress), net.ParseIP(newCertSAN)}
|
|
expectedNewIPs := append(newIPs, expectedAPIServerAltIPs...)
|
|
|
|
if certConfig.Name != expectedCertName {
|
|
t.Errorf("expected Name to be %s, got %s", expectedCertName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.CAName != constants.CaCertAndKeyName {
|
|
t.Errorf("expected CAName to be %s, got %s", constants.CaCertAndKeyName, certConfig.CAName)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Organization, expectedOrganization) {
|
|
t.Errorf("expected Organization to contain %v, got %v", expectedOrganization, certConfig.Config.Organization)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedAPIServerAltDNSNames) {
|
|
t.Errorf("expected DNSNames to contain %v, got %v", expectedAPIServerAltDNSNames, certConfig.Config.AltNames.DNSNames)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedNewIPs) {
|
|
t.Errorf("expected IPs to contain %v, got %v", expectedNewIPs, certConfig.Config.AltNames.IPs)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertFrontProxyCA(t *testing.T) {
|
|
certConfig := KarmadaCertFrontProxyCA()
|
|
|
|
expectedCommonName := "front-proxy-ca"
|
|
|
|
if certConfig.Name != constants.FrontProxyCaCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.FrontProxyCaCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertFrontProxyClient(t *testing.T) {
|
|
certConfig := KarmadaCertFrontProxyClient()
|
|
|
|
expectedCommonName := "front-proxy-client"
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
|
|
|
if certConfig.Name != constants.FrontProxyClientCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.FrontProxyClientCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.CAName != constants.FrontProxyCaCertAndKeyName {
|
|
t.Errorf("expected CAName to be %s, got %s", constants.FrontProxyCaCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertEtcdCA(t *testing.T) {
|
|
certConfig := KarmadaCertEtcdCA()
|
|
|
|
expectedCommonName := "karmada-etcd-ca"
|
|
|
|
if certConfig.Name != constants.EtcdCaCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.EtcdCaCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertEtcdServer(t *testing.T) {
|
|
certConfig := KarmadaCertEtcdServer()
|
|
|
|
cfg := &AltNamesMutatorConfig{Namespace: constants.KarmadaSystemNamespace}
|
|
err := certConfig.AltNamesMutatorFunc(cfg, certConfig)
|
|
if err != nil {
|
|
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
|
}
|
|
|
|
expectedCommonName := "karmada-etcd-server"
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
|
expectedDNSNames := []string{
|
|
"localhost",
|
|
fmt.Sprintf("%s.%s.svc.cluster.local", util.KarmadaEtcdClientName(cfg.Name), cfg.Namespace),
|
|
fmt.Sprintf("*.%s.%s.svc.cluster.local", util.KarmadaEtcdName(cfg.Name), cfg.Namespace),
|
|
}
|
|
expectedIPs := []net.IP{net.IPv4(127, 0, 0, 1)}
|
|
|
|
if certConfig.Name != constants.EtcdServerCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.EtcdServerCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.CAName != constants.EtcdCaCertAndKeyName {
|
|
t.Errorf("expected CAName to be %s, got %s", constants.EtcdCaCertAndKeyName, certConfig.CAName)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth and ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedDNSNames) {
|
|
t.Errorf("expected DNSNames to contain %v, got %v", expectedDNSNames, certConfig.Config.AltNames.DNSNames)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedIPs) {
|
|
t.Errorf("expected IPs to contain %v, got %v", expectedIPs, certConfig.Config.AltNames.IPs)
|
|
}
|
|
}
|
|
|
|
func TestKarmadaCertEtcdClient(t *testing.T) {
|
|
certConfig := KarmadaCertEtcdClient()
|
|
|
|
expectedCommonName := "karmada-etcd-client"
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
|
|
|
if certConfig.Name != constants.EtcdClientCertAndKeyName {
|
|
t.Errorf("expected Name to be %s, got %s", constants.EtcdClientCertAndKeyName, certConfig.Name)
|
|
}
|
|
|
|
if certConfig.CAName != constants.EtcdCaCertAndKeyName {
|
|
t.Errorf("expected CAName to be %s, got %s", constants.EtcdCaCertAndKeyName, certConfig.CAName)
|
|
}
|
|
|
|
if certConfig.Config.CommonName != expectedCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth and ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
|
}
|
|
}
|
|
|
|
func TestGeneratePrivateKey_ECDSAKey_ShouldReturnValidECDSAKey(t *testing.T) {
|
|
key, err := GeneratePrivateKey(x509.ECDSA)
|
|
if err != nil {
|
|
t.Fatalf("GeneratePrivateKey() returned error: %v", err)
|
|
}
|
|
|
|
// Check that the key is of type *ecdsa.PrivateKey.
|
|
if _, ok := key.(*ecdsa.PrivateKey); !ok {
|
|
t.Errorf("GeneratePrivateKey() returned key of type %T, expected *ecdsa.PrivateKey", key)
|
|
}
|
|
|
|
// Verify that the elliptic curve is P-256 (secp256r1).
|
|
ecdsaKey := key.(*ecdsa.PrivateKey)
|
|
if ecdsaKey.Curve != elliptic.P256() {
|
|
t.Errorf("GeneratePrivateKey() returned ECDSA key with elliptic curve %v, expected P-256", ecdsaKey.Curve)
|
|
}
|
|
}
|
|
|
|
func TestGeneratePrivateKey_RSAKey_ShouldReturnValidRSAKey(t *testing.T) {
|
|
expectedRSAKeySizeInBytes := rsaKeySize / 8
|
|
|
|
key, err := GeneratePrivateKey(x509.RSA)
|
|
if err != nil {
|
|
t.Fatalf("GeneratePrivateKey() returned error: %v", err)
|
|
}
|
|
|
|
// Check that the key is of type *rsa.PrivateKey.
|
|
if _, ok := key.(*rsa.PrivateKey); !ok {
|
|
t.Errorf("GeneratePrivateKey() returned key of type %T, expected *rsa.PrivateKey", key)
|
|
}
|
|
|
|
// Verify that the key size is correct.
|
|
rsaKey := key.(*rsa.PrivateKey)
|
|
if rsaKey.Size() != expectedRSAKeySizeInBytes {
|
|
t.Errorf("GeneratePrivateKey() returned RSA key of size %d, expected %d", rsaKey.Size()*8, rsaKeySize)
|
|
}
|
|
}
|
|
|
|
func TestGeneratePrivateKey_InvalidKeyType_ShouldReturnError(t *testing.T) {
|
|
_, err := GeneratePrivateKey(x509.DSA)
|
|
if err == nil {
|
|
t.Error("GeneratePrivateKey() expected error for unsupported key type 'DSA', got nil")
|
|
}
|
|
}
|
|
|
|
func TestEncodeCertPEM_CorrectEncoding(t *testing.T) {
|
|
cert := &x509.Certificate{
|
|
Raw: []byte("dummy_certificate_data"),
|
|
}
|
|
|
|
// Base64 encoding of the dummy certificate data.
|
|
base64EncodedCert := base64.StdEncoding.EncodeToString(cert.Raw)
|
|
|
|
// Construct the expected PEM format.
|
|
expectedPEM := fmt.Sprintf(
|
|
"-----BEGIN %s-----\n%s\n-----END %s-----\n",
|
|
CertificateBlockType,
|
|
base64EncodedCert,
|
|
CertificateBlockType,
|
|
)
|
|
got := EncodeCertPEM(cert)
|
|
|
|
// Check if the encoded PEM matches the expected PEM format.
|
|
if !bytes.Equal(got, []byte(expectedPEM)) {
|
|
t.Errorf("EncodeCertPEM() = %s; want %s", got, expectedPEM)
|
|
}
|
|
}
|
|
|
|
func TestNewCertificateAuthority(t *testing.T) {
|
|
cc := &CertConfig{
|
|
Name: "test-ca",
|
|
CAName: "test-ca",
|
|
PublicKeyAlgorithm: x509.RSA,
|
|
Config: certutil.Config{
|
|
CommonName: "test-ca",
|
|
Organization: []string{"test-org"},
|
|
},
|
|
}
|
|
|
|
cert, err := NewCertificateAuthority(cc)
|
|
if err != nil {
|
|
t.Fatalf("NewCertificateAuthority() returned an error: %v", err)
|
|
}
|
|
|
|
if cert == nil {
|
|
t.Fatal("NewCertificateAuthority() returned nil cert")
|
|
}
|
|
|
|
if cert.PairName != cc.Name {
|
|
t.Errorf("expected pairName to be %s, got %s", cc.Name, cert.PairName)
|
|
}
|
|
|
|
if cert.CAName != cc.CAName {
|
|
t.Errorf("expected caName to be %s, got %s", cc.CAName, cert.CAName)
|
|
}
|
|
|
|
if cert.Cert == nil {
|
|
t.Error("expected cert to be non-nil")
|
|
}
|
|
|
|
if cert.Key == nil {
|
|
t.Error("expected key to be non-nil")
|
|
}
|
|
|
|
block, _ := pem.Decode(cert.Cert)
|
|
if block == nil || block.Type != CertificateBlockType {
|
|
t.Errorf("expected PEM block type to be %s, got %v", CertificateBlockType, block)
|
|
}
|
|
}
|
|
|
|
func TestParsePrivateKeyPEM_ValidRSAKey(t *testing.T) {
|
|
key, err := GeneratePrivateKey(x509.RSA)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate RSA key: %v", err)
|
|
}
|
|
|
|
keyPEM, err := keyutil.MarshalPrivateKeyToPEM(key)
|
|
if err != nil {
|
|
t.Errorf("unable to marshal private key to PEM, err: %v", err)
|
|
}
|
|
|
|
signer, err := ParsePrivateKeyPEM(keyPEM)
|
|
if err != nil {
|
|
t.Fatalf("ParsePrivateKeyPEM() returned an error: %v", err)
|
|
}
|
|
|
|
if _, ok := signer.(*rsa.PrivateKey); !ok {
|
|
t.Errorf("ParsePrivateKeyPEM() returned key of type %T, expected *rsa.PrivateKey", signer)
|
|
}
|
|
}
|
|
|
|
func TestParsePrivateKeyPEM_InvalidKeyType(t *testing.T) {
|
|
invalidKeyPEM := pem.EncodeToMemory(&pem.Block{
|
|
Type: "INVALID PRIVATE KEY",
|
|
Bytes: []byte("invalid"),
|
|
})
|
|
|
|
signer, err := ParsePrivateKeyPEM(invalidKeyPEM)
|
|
if err == nil {
|
|
t.Errorf("ParsePrivateKeyPEM() expected error for unsupported key type, got nil")
|
|
}
|
|
if signer != nil {
|
|
t.Errorf("ParsePrivateKeyPEM() expected nil signer, got %v", signer)
|
|
}
|
|
}
|
|
|
|
func TestCreateCertAndKeyFilesWithCA(t *testing.T) {
|
|
caName, caCommonName := "test-ca", "test-ca"
|
|
caCertConfig := &CertConfig{
|
|
Name: caName,
|
|
CAName: caName,
|
|
PublicKeyAlgorithm: x509.RSA,
|
|
Config: certutil.Config{
|
|
CommonName: caCommonName,
|
|
Organization: []string{"test-org"},
|
|
},
|
|
}
|
|
|
|
caCert, err := NewCertificateAuthority(caCertConfig)
|
|
if err != nil {
|
|
t.Fatalf("NewCertificateAuthority() returned an error: %v", err)
|
|
}
|
|
|
|
certName, certCommonName := "test-cert", "test-common-name"
|
|
certConfig := &CertConfig{
|
|
Name: certName,
|
|
CAName: caName,
|
|
PublicKeyAlgorithm: x509.RSA,
|
|
Config: certutil.Config{
|
|
CommonName: certCommonName,
|
|
Organization: []string{"test-org"},
|
|
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
},
|
|
}
|
|
|
|
cert, err := CreateCertAndKeyFilesWithCA(certConfig, caCert.CertData(), caCert.KeyData())
|
|
if err != nil {
|
|
t.Fatalf("CreateCertAndKeyFilesWithCA() returned an error: %v", err)
|
|
}
|
|
|
|
if cert == nil {
|
|
t.Fatal("CreateCertAndKeyFilesWithCA() returned nil cert")
|
|
}
|
|
|
|
if cert.Cert == nil || cert.Key == nil {
|
|
t.Error("Expected cert and key to be non-nil")
|
|
}
|
|
|
|
if cert.PairName != certConfig.Name {
|
|
t.Errorf("expected pairName to be %s, got %s", certConfig.Name, cert.PairName)
|
|
}
|
|
|
|
if cert.CAName != certConfig.CAName {
|
|
t.Errorf("expected caName to be %s, got %s", certConfig.CAName, cert.CAName)
|
|
}
|
|
|
|
block, _ := pem.Decode(cert.Cert)
|
|
if block == nil || block.Type != CertificateBlockType {
|
|
t.Errorf("expected PEM block type to be %s, got %v", CertificateBlockType, block)
|
|
}
|
|
}
|
|
|
|
func TestNewSignedCert_Success(t *testing.T) {
|
|
// Create a CA certificate.
|
|
caName, caCommonName := "test-ca", "test-ca"
|
|
caCertConfig := &CertConfig{
|
|
Name: caName,
|
|
CAName: caName,
|
|
PublicKeyAlgorithm: x509.RSA,
|
|
Config: certutil.Config{
|
|
CommonName: caCommonName,
|
|
Organization: []string{"test-org"},
|
|
},
|
|
}
|
|
|
|
caKarmadaCert, err := NewCertificateAuthority(caCertConfig)
|
|
if err != nil {
|
|
t.Fatalf("NewCertificateAuthority() returned an error: %v", err)
|
|
}
|
|
|
|
caCerts, err := certutil.ParseCertsPEM(caKarmadaCert.CertData())
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
caCert := caCerts[0]
|
|
|
|
caKey, err := ParsePrivateKeyPEM(caKarmadaCert.Key)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// Create a key pair for the certificate.
|
|
key, err := GeneratePrivateKey(x509.RSA)
|
|
if err != nil {
|
|
t.Fatalf("failed to generate key: %v", err)
|
|
}
|
|
|
|
// Create a CertConfig for the test certificate.
|
|
expectedCertCommonName := "test-cert"
|
|
expectedCertOrganization := []string{"test-org"}
|
|
expectedCertDNSNames := []string{"localhost"}
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
|
certNotAfter := time.Now().Add(constants.CertificateValidity)
|
|
cc := &CertConfig{
|
|
Config: certutil.Config{
|
|
CommonName: expectedCertCommonName,
|
|
Organization: expectedCertOrganization,
|
|
AltNames: certutil.AltNames{DNSNames: expectedCertDNSNames},
|
|
Usages: expectedUsages,
|
|
},
|
|
NotAfter: &certNotAfter,
|
|
}
|
|
|
|
cert, err := NewSignedCert(cc, key, caCert, caKey, false)
|
|
if err != nil {
|
|
t.Fatalf("NewSignedCert returned an error: %v", err)
|
|
}
|
|
|
|
if cert.IsCA {
|
|
t.Errorf("expected certificate to not be a CA, but it was")
|
|
}
|
|
|
|
if cert.Subject.CommonName != expectedCertCommonName {
|
|
t.Errorf("expected CommonName to be %s, got %s", expectedCertCommonName, cert.Subject.CommonName)
|
|
}
|
|
|
|
if !util.ContainsAllValues(cert.Subject.Organization, expectedCertOrganization) {
|
|
t.Errorf("expected Organization to contain %v, got %v", expectedCertOrganization, cert.Subject.Organization)
|
|
}
|
|
|
|
if !util.ContainsAllValues(cert.DNSNames, expectedCertDNSNames) {
|
|
t.Errorf("expected DNSNames to contain %v, got %v", expectedCertDNSNames, cert.DNSNames)
|
|
}
|
|
|
|
if !util.ContainsAllValues(cert.ExtKeyUsage, expectedUsages) {
|
|
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth, got %v", cert.ExtKeyUsage)
|
|
}
|
|
}
|
|
|
|
func TestNewSignedCert_ErrorOnEmptyCommonName(t *testing.T) {
|
|
// Create a key pair for the certificate and CA.
|
|
key, err := GeneratePrivateKey(x509.RSA)
|
|
if err != nil {
|
|
t.Fatalf("failed to generate key: %v", err)
|
|
}
|
|
|
|
// Create a CertConfig for the test certificate.
|
|
expectedCertOrganization := []string{"test-org"}
|
|
expectedCertDNSNames := []string{"localhost"}
|
|
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
|
certNotAfter := time.Now().Add(constants.CertificateValidity)
|
|
cc := &CertConfig{
|
|
Config: certutil.Config{
|
|
CommonName: "",
|
|
Organization: expectedCertOrganization,
|
|
AltNames: certutil.AltNames{DNSNames: expectedCertDNSNames},
|
|
Usages: expectedUsages,
|
|
},
|
|
NotAfter: &certNotAfter,
|
|
}
|
|
|
|
_, err = NewSignedCert(cc, key, &x509.Certificate{}, key, false)
|
|
if err == nil {
|
|
t.Fatalf("expected error for empty CommonName, but got nil")
|
|
}
|
|
|
|
expectedErrorMsg := "must specify a CommonName"
|
|
if err.Error() != expectedErrorMsg {
|
|
t.Errorf("expected error %s, got %v", expectedErrorMsg, err)
|
|
}
|
|
}
|
|
|
|
func TestAppendSANsToAltNames(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
SANs []string
|
|
wantIPs []net.IP
|
|
wantDNS []string
|
|
}{
|
|
{
|
|
name: "AppendSANsToAltNames_ValidIPAddress_ShouldReturnCorrectIPs",
|
|
SANs: []string{"192.168.1.1"},
|
|
wantIPs: []net.IP{net.ParseIP("192.168.1.1")},
|
|
wantDNS: nil,
|
|
},
|
|
{
|
|
name: "AppendSANsToAltNames_ValidDNSName_ShouldReturnCorrectDNS",
|
|
SANs: []string{"example.com"},
|
|
wantIPs: nil,
|
|
wantDNS: []string{"example.com"},
|
|
},
|
|
{
|
|
name: "AppendSANsToAltNames_InvalidDNSAndValidIP_ShouldIgnoreInvalidDNSAndReturnCorrectIP",
|
|
SANs: []string{"invalid!.com", "10.0.0.1"},
|
|
wantIPs: []net.IP{net.ParseIP("10.0.0.1")},
|
|
wantDNS: nil,
|
|
},
|
|
{
|
|
name: "AppendSANsToAltNames_ValidWildcardDNS_ShouldReturnCorrectWildcardDNS",
|
|
SANs: []string{"*.example.com"},
|
|
wantIPs: nil,
|
|
wantDNS: []string{"*.example.com"},
|
|
},
|
|
{
|
|
name: "AppendSANsToAltNames_MixedValidIPAndDNS_ShouldReturnCorrectIPAndDNS",
|
|
SANs: []string{"example.com", "10.0.0.1"},
|
|
wantIPs: []net.IP{net.ParseIP("10.0.0.1")},
|
|
wantDNS: []string{"example.com"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
altNames := &certutil.AltNames{}
|
|
appendSANsToAltNames(altNames, tt.SANs)
|
|
|
|
if !reflect.DeepEqual(altNames.IPs, tt.wantIPs) {
|
|
t.Errorf("AppendSANsToAltNames() IPs = %+v, want %+v", altNames.IPs, tt.wantIPs)
|
|
}
|
|
if !reflect.DeepEqual(altNames.DNSNames, tt.wantDNS) {
|
|
t.Errorf("AppendSANsToAltNames() DNS = %+v, want %+v", altNames.IPs, tt.wantIPs)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRemoveDuplicateAltNames(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input *certutil.AltNames
|
|
want *certutil.AltNames
|
|
}{
|
|
{
|
|
name: "RemoveDuplicateAltNames_DuplicateDNSNames_ShouldReturnUniqueDNSNames",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
|
},
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_DuplicateIPs_ShouldReturnUniqueIPs",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.1")},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1")},
|
|
},
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_DuplicateDNSNamesAndIPs_ShouldReturnUniqueDNSNamesAndIPs",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
|
},
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_NoDuplicates_ShouldReturnSameAltNames",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.org"},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
|
},
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_EmptyAltNames_ShouldReturnEmptyAltNames",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{},
|
|
IPs: []net.IP{},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{},
|
|
IPs: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_NilAltNames_ShouldReturnNil",
|
|
input: nil,
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_EmptyDNSNamesWithNonEmptyIPs_ShouldReturnUniqueIPs",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.1")},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{},
|
|
IPs: []net.IP{net.ParseIP("192.168.1.1")},
|
|
},
|
|
},
|
|
{
|
|
name: "RemoveDuplicateAltNames_EmptyIPsWithNonEmptyDNSNames_ShouldReturnUniqueDNSNames",
|
|
input: &certutil.AltNames{
|
|
DNSNames: []string{"example.com", "example.com"},
|
|
IPs: []net.IP{},
|
|
},
|
|
want: &certutil.AltNames{
|
|
DNSNames: []string{"example.com"},
|
|
IPs: nil,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
RemoveDuplicateAltNames(tt.input)
|
|
if !reflect.DeepEqual(tt.input, tt.want) {
|
|
t.Errorf("RemoveDuplicateAltNames() = %+v, want %+v", tt.input, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|