From a824b75bfa479c5e40f74398b82fec7ae9c6981d Mon Sep 17 00:00:00 2001 From: SHASHIKANTH THAMBRAHALLI Date: Tue, 1 Nov 2022 05:50:46 +0530 Subject: [PATCH] First part of the changes for SSLKEYR (#328) * Squashed all commits * Addressed review comments --- internal/tls/tls.go | 74 ++++++++++++++++++++++++++--------- internal/tls/tls_web.go | 1 - test/docker/devconfig_test.go | 74 +++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 19 deletions(-) diff --git a/internal/tls/tls.go b/internal/tls/tls.go index 96ca64d..32fb105 100644 --- a/internal/tls/tls.go +++ b/internal/tls/tls.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2019, 2021 +© Copyright IBM Corporation 2019, 2022 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -76,18 +76,20 @@ type TLSStore struct { Truststore KeyStoreData } -func configureTLSKeystores(keystoreDir, keyDir, trustDir string, p12TruststoreRequired bool) (string, KeyStoreData, KeyStoreData, error) { - +func configureTLSKeystores(keystoreDir, keyDir, trustDir string, p12TruststoreRequired bool, nativeTLSHA bool) (string, KeyStoreData, KeyStoreData, error) { + var keyLabel string // Create the CMS Keystore & PKCS#12 Truststore (if required) - tlsStore, err := generateAllKeystores(keystoreDir, p12TruststoreRequired) + tlsStore, err := generateAllKeystores(keystoreDir, p12TruststoreRequired, nativeTLSHA) if err != nil { return "", tlsStore.Keystore, tlsStore.Truststore, err } - // Process all keys - add them to the CMS KeyStore - keyLabel, err := processKeys(&tlsStore, keystoreDir, keyDir) - if err != nil { - return "", tlsStore.Keystore, tlsStore.Truststore, err + if tlsStore.Keystore.Keystore != nil { + // Process all keys - add them to the CMS KeyStore + keyLabel, err = processKeys(&tlsStore, keystoreDir, keyDir) + if err != nil { + return "", tlsStore.Keystore, tlsStore.Truststore, err + } } // Process all trust certificates - add them to the CMS KeyStore & PKCS#12 Truststore (if required) @@ -101,13 +103,13 @@ func configureTLSKeystores(keystoreDir, keyDir, trustDir string, p12TruststoreRe // ConfigureDefaultTLSKeystores configures the CMS Keystore & PKCS#12 Truststore func ConfigureDefaultTLSKeystores() (string, KeyStoreData, KeyStoreData, error) { - return configureTLSKeystores(keystoreDirDefault, keyDirDefault, trustDirDefault, true) + return configureTLSKeystores(keystoreDirDefault, keyDirDefault, trustDirDefault, true, false) } // ConfigureHATLSKeystore configures the CMS Keystore & PKCS#12 Truststore func ConfigureHATLSKeystore() (string, KeyStoreData, KeyStoreData, error) { // *.crt files mounted to the HA TLS dir keyDirHA will be processed as trusted in the CMS keystore - return configureTLSKeystores(keystoreDirHA, keyDirHA, keyDirHA, false) + return configureTLSKeystores(keystoreDirHA, keyDirHA, keyDirHA, false, true) } // ConfigureTLS configures TLS for the queue manager @@ -115,9 +117,18 @@ func ConfigureTLS(keyLabel string, cmsKeystore KeyStoreData, devMode bool, log * const mqsc string = "/etc/mqm/15-tls.mqsc" const mqscTemplate string = mqsc + ".tpl" + sslKeyRing := "" + // Don't set SSLKEYR if no keys or crts are not supplied + // Key label will be blank if no certs were added during processing keys and certs. + if cmsKeystore.Keystore != nil { + certList, _ := cmsKeystore.Keystore.ListAllCertificates() + if len(certList) > 0 { + sslKeyRing = strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb") + } + } err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{ - "SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"), + "SSLKeyR": sslKeyRing, "CertificateLabel": keyLabel, }, log) if err != nil { @@ -159,7 +170,7 @@ func configureTLSDev(log *logger.Logger) error { } // generateAllKeystores creates the CMS Keystore & PKCS#12 Truststore (if required) -func generateAllKeystores(keystoreDir string, p12TruststoreRequired bool) (TLSStore, error) { +func generateAllKeystores(keystoreDir string, p12TruststoreRequired bool, nativeTLSHA bool) (TLSStore, error) { var cmsKeystore, p12Truststore KeyStoreData @@ -175,11 +186,19 @@ func generateAllKeystores(keystoreDir string, p12TruststoreRequired bool) (TLSSt return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create Keystore directory: %v", err) } - // Create the CMS Keystore - cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password) - err = cmsKeystore.Keystore.Create() - if err != nil { - return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create CMS Keystore: %v", err) + // Search the default keys directory for any keys/certs. + keysDirectory := keyDirDefault + // Change to default native HA TLS directory if we are configuring nativeHA + if nativeTLSHA { + keysDirectory = keyDirHA + } + // Create the CMS Keystore if we have been provided keys and certificates + if haveKeysAndCerts(keysDirectory) || haveKeysAndCerts(trustDirDefault) { + cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password) + err = cmsKeystore.Keystore.Create() + if err != nil { + return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create CMS Keystore: %v", err) + } } // Create the PKCS#12 Truststore (if required) @@ -203,7 +222,6 @@ func processKeys(tlsStore *TLSStore, keystoreDir string, keyDir string) (string, // Process all keys keyList, err := ioutil.ReadDir(keyDir) if err == nil && len(keyList) > 0 { - // Process each set of keys - each set should contain files: *.key & *.crt for _, keySet := range keyList { keys, _ := ioutil.ReadDir(filepath.Join(keyDir, keySet.Name())) @@ -602,3 +620,23 @@ func writeCertificatesToFile(file string, certificates []*pem.Block) error { } return nil } + +// Search the specified directory for .key and .crt files. +// Return true if at least one .key or .crt file is found else false +func haveKeysAndCerts(keyDir string) bool { + fileList, err := os.ReadDir(keyDir) + if err == nil && len(fileList) > 0 { + for _, fileInfo := range fileList { + // Keys and certs will be supplied in an user defined subdirectory. + // Do a listing of the subdirectory and then search for .key and .cert files + keys, _ := ioutil.ReadDir(filepath.Join(keyDir, fileInfo.Name())) + for _, key := range keys { + if strings.Contains(key.Name(), ".key") || strings.Contains(key.Name(), ".crt") { + // We found at least one key/crt file. + return true + } + } + } + } + return false +} diff --git a/internal/tls/tls_web.go b/internal/tls/tls_web.go index 2b84af8..3789114 100644 --- a/internal/tls/tls_web.go +++ b/internal/tls/tls_web.go @@ -65,7 +65,6 @@ func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (strin // Check if a new self-signed certificate should be generated genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") if genHostName != "" { - // Create the Web Keystore newWebKeystore := keystore.NewPKCS12KeyStore(webKeystoreFile, p12Truststore.Password) err := newWebKeystore.Create() diff --git a/test/docker/devconfig_test.go b/test/docker/devconfig_test.go index f7a29d5..4cf827c 100644 --- a/test/docker/devconfig_test.go +++ b/test/docker/devconfig_test.go @@ -1,3 +1,4 @@ +//go:build mqdev // +build mqdev /* @@ -184,3 +185,76 @@ func TestDevConfigDisabled(t *testing.T) { // Stop the container cleanly stopContainer(t, cli, id) } + +// Test if SSLKEYR and CERTLABL attributes are not set when key and certificate +// are not supplied. +func TestSSLKEYRBlank(t *testing.T) { + t.Parallel() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_QMGR_NAME=qm1", + "MQ_ENABLE_EMBEDDED_WEB_SERVER=false", + }, + } + id := runContainerWithPorts(t, cli, &containerConfig, []int{9443}) + defer cleanContainer(t, cli, id) + waitForReady(t, cli, id) + // execute runmqsc to display qmgr SSLKEYR and CERTLABL attibutes. + // Search the console output for exepcted values + _, sslkeyROutput := execContainer(t, cli, id, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"}) + if !strings.Contains(sslkeyROutput, "SSLKEYR( )") && !strings.Contains(sslkeyROutput, "CERTLABL( )") { + t.Errorf("Expected SSLKEYR to be blank but it is not; got \"%v\"", sslkeyROutput) + } + + // Stop the container cleanly + stopContainer(t, cli, id) +} + +// Test if SSLKEYR and CERTLABL attributes are set when key and certificate +// are supplied. +func TestSSLKEYRWithSuppliedKeyAndCert(t *testing.T) { + t.Parallel() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_QMGR_NAME=QM1", + "MQ_ENABLE_EMBEDDED_WEB_SERVER=false", + }, + Image: imageName(), + } + hostConfig := container.HostConfig{ + Binds: []string{ + coverageBind(t), + tlsDir(t, false) + ":/etc/mqm/pki/keys/default", + }, + } + networkingConfig := network.NetworkingConfig{} + ctr, err := cli.ContainerCreate(context.Background(), &containerConfig, &hostConfig, &networkingConfig, t.Name()) + if err != nil { + t.Fatal(err) + } + defer cleanContainer(t, cli, ctr.ID) + startContainer(t, cli, ctr.ID) + waitForReady(t, cli, ctr.ID) + // execute runmqsc to display qmgr SSLKEYR and CERTLABL attibutes. + // Search the console output for exepcted values + _, sslkeyROutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL' | runmqsc"}) + if !strings.Contains(sslkeyROutput, "SSLKEYR(/run/runmqserver/tls/key)") && !strings.Contains(sslkeyROutput, "CERTLABL(default)") { + t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslkeyROutput) + } + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +}