From 794d1ed2b2f516efbb69bcc5d17b258297d888e7 Mon Sep 17 00:00:00 2001 From: SHASHIKANTH THAMBRAHALLI Date: Sat, 17 Dec 2022 10:09:41 +0530 Subject: [PATCH] PR for FIPS implemenation (#351) * Part 1 of FIPS Compliance * MQ Web Server FIPSs changes * Remove function param * Updates to FIPS MQ WebServer * Fix build error * Merge latest code from private-master * Rename fips variable * Fix build break * Fix build break * Fix build break * Add new docker tests * First cut of fips metrics * First cut of fips metrics * Second part of metrics fips * Second part of metrics fips * Added NativeHA FIPS * Updated test * Add Native HA tests * Optimze FIPS handling * Update comments * Apply changes from private-master * Undo metrics changes * Merge latest changes * Pull in changes from master * Update copyright year * Resolve merge conflicts --- Makefile | 2 +- cmd/runmqserver/main.go | 14 +- cmd/runmqserver/post_init.go | 12 +- cmd/runmqserver/webserver.go | 24 +- etc/mqm/15-tls.mqsc.tpl | 3 +- ha/native-ha.ini.tpl | 3 + internal/fips/fips.go | 78 ++++ internal/fips/fips_test.go | 65 +++ internal/ha/ha.go | 10 +- internal/keystore/keystore.go | 52 ++- internal/tls/tls.go | 13 +- internal/tls/tls_web.go | 1 - test/docker/devconfig_test.go | 419 +++++++++++++++++- test/docker/devconfig_test_util.go | 56 ++- test/docker/docker_api_test.go | 9 +- test/docker/docker_api_test_util.go | 39 ++ test/docker/mq_native_ha_test.go | 97 +++- test/docker/mq_native_ha_test_util.go | 2 +- test/tlsinvalidcert/expired.key | 30 ++ test/tlsinvalidcert/expired.p12 | Bin 0 -> 2733 bytes test/tlsinvalidcert/expired.pem | 26 ++ test/tlsinvalidcert/expiredCA.p12 | Bin 0 -> 4317 bytes test/tlsinvalidcert/expiredCA.pem | 35 ++ .../configDropins/defaults/jvm.options.tpl | 5 + 24 files changed, 956 insertions(+), 39 deletions(-) create mode 100644 internal/fips/fips.go create mode 100644 internal/fips/fips_test.go create mode 100644 test/tlsinvalidcert/expired.key create mode 100644 test/tlsinvalidcert/expired.p12 create mode 100644 test/tlsinvalidcert/expired.pem create mode 100644 test/tlsinvalidcert/expiredCA.p12 create mode 100644 test/tlsinvalidcert/expiredCA.pem create mode 100644 web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options.tpl diff --git a/Makefile b/Makefile index c8bd0a2..fbe42b8 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION)) # Options to `go test` for the Docker tests TEST_OPTS_DOCKER ?= # Timeout for the Docker tests -TEST_TIMEOUT_DOCKER ?= 30m +TEST_TIMEOUT_DOCKER ?= 45m # MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server # MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index 4885ce4..bd330fa 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2017, 2021 +© Copyright IBM Corporation 2017, 2022 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import ( "os" "sync" + "github.com/ibm-messaging/mq-container/internal/fips" "github.com/ibm-messaging/mq-container/internal/ha" "github.com/ibm-messaging/mq-container/internal/metrics" "github.com/ibm-messaging/mq-container/internal/ready" @@ -144,6 +145,9 @@ func doMain() error { // Print out versioning information logVersionInfo() + // Determine FIPS compliance level + fips.ProcessFIPSType(log) + keyLabel, defaultCmsKeystore, defaultP12Truststore, err := tls.ConfigureDefaultTLSKeystores() if err != nil { logTermination(err) @@ -170,6 +174,14 @@ func doMain() error { } } + // Log a message on the console to indicate FIPS certified + // cryptography being used. + if fips.IsFIPSEnabled() { + log.Println("FIPS cryptography is enabled.") + } else { + log.Println("FIPS cryptography is not enabled.") + } + enableTraceCrtmqm := os.Getenv("MQ_ENABLE_TRACE_CRTMQM") if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" { err = startMQTrace() diff --git a/cmd/runmqserver/post_init.go b/cmd/runmqserver/post_init.go index 7128e1e..ffccaea 100644 --- a/cmd/runmqserver/post_init.go +++ b/cmd/runmqserver/post_init.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2018, 2019 +© Copyright IBM Corporation 2018, 2022 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package main import ( "os" + "github.com/ibm-messaging/mq-container/internal/fips" "github.com/ibm-messaging/mq-container/internal/tls" ) @@ -35,6 +36,15 @@ func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error { if len(p12Truststore.TrustedCerts) == 0 { webTruststoreRef = "MQWebKeyStore" } + + // Enable FIPS for MQ Web Server if asked for. + if fips.IsFIPSEnabled() { + err = configureFIPSWebServer(p12Truststore) + if err != nil { + return err + } + } + // Start the web server, in the background (if installed) // WARNING: No error handling or health checking available for the web server go func() { diff --git a/cmd/runmqserver/webserver.go b/cmd/runmqserver/webserver.go index 4450b03..174302c 100644 --- a/cmd/runmqserver/webserver.go +++ b/cmd/runmqserver/webserver.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2018, 2020 +© Copyright IBM Corporation 2018, 2022 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -197,3 +197,25 @@ func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string return webKeystore, err } + +// Configure FIPS mode for MQ Web Server +func configureFIPSWebServer(p12TrustStore tls.KeyStoreData) error { + var errOut error + // Need to update jvm.options file of MQ Web Server. We don't update the jvm.options file + // in /var/mqm/web/installations/Installation1/servers/mqweb directory. Instead we update + // the one in /var/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults. + // During runtime MQ Web Server merges the data from two files. + mqwebJvmOptsDir := "/var/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults" + _, errOut = os.Stat(mqwebJvmOptsDir) + if errOut == nil { + // Update the jvm.options file using the data from template file. Tell the MQ Web Server + // use a FIPS provider by setting "-Dcom.ibm.jsse2.usefipsprovider=true" and then tell it + // use a specific FIPS provider by setting "Dcom.ibm.jsse2.usefipsProviderName=IBMJCEPlusFIPS". + errOut = mqtemplate.ProcessTemplateFile(mqwebJvmOptsDir+"/jvm.options.tpl", + mqwebJvmOptsDir+"/jvm.options", map[string]string{ + "FipsProvider": "true", + "FipsProviderName": "IBMJCEPlusFIPS", + }, log) + } + return errOut +} diff --git a/etc/mqm/15-tls.mqsc.tpl b/etc/mqm/15-tls.mqsc.tpl index 745f10b..1dbbd1a 100644 --- a/etc/mqm/15-tls.mqsc.tpl +++ b/etc/mqm/15-tls.mqsc.tpl @@ -1,4 +1,4 @@ -* © Copyright IBM Corporation 2019 +* © Copyright IBM Corporation 2019, 2022 * * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,4 +16,5 @@ * Set the keystore location for the queue manager ALTER QMGR SSLKEYR('{{ .SSLKeyR }}') ALTER QMGR CERTLABL('{{ .CertificateLabel }}') +ALTER QMGR SSLFIPS({{ .SSLFips }}) REFRESH SECURITY(*) TYPE(SSL) diff --git a/ha/native-ha.ini.tpl b/ha/native-ha.ini.tpl index b6be471..7d42900 100644 --- a/ha/native-ha.ini.tpl +++ b/ha/native-ha.ini.tpl @@ -6,6 +6,9 @@ NativeHALocalInstance: {{ if .CipherSpec }} CipherSpec={{ .CipherSpec }} {{- end }} + {{ if .SSLFipsRequired }} + SSLFipsRequired={{ .SSLFipsRequired }} + {{- end }} {{- end }} NativeHAInstance: Name={{ .NativeHAInstance0_Name }} diff --git a/internal/fips/fips.go b/internal/fips/fips.go new file mode 100644 index 0000000..70987bf --- /dev/null +++ b/internal/fips/fips.go @@ -0,0 +1,78 @@ +/* +© Copyright IBM Corporation 2022 + +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 fips + +import ( + "os" + "strings" + + "github.com/ibm-messaging/mq-container/internal/command" + "github.com/ibm-messaging/mq-container/pkg/logger" +) + +var ( + FIPSEnabledType int +) + +// FIPS has been turned off either because OS is not FIPS enabled or +// MQ_ENABLE_FIPS environment variable is set to "false" +const FIPS_ENABLED_OFF = 0 + +// FIPS is turned ON +const FIPS_ENABLED_ON = 1 + +// FIPS enabled at operating system level +const FIPS_ENABLED_PLATFORM = 1 + +// FIPS enabled via environment variable +const FIPS_ENABLED_ENV_VAR = 2 + +// Get FIPS enabled type. +func ProcessFIPSType(logs *logger.Logger) { + // Run "sysctl crypto.fips_enabled" command to determine if FIPS has been enabled + // on OS. + FIPSEnabledType = FIPS_ENABLED_OFF + out, _, err := command.Run("sysctl", "crypto.fips_enabled") + if err == nil { + // Check the output of the command for expected output + if strings.Contains(out, "crypto.fips_enabled = 1") { + FIPSEnabledType = FIPS_ENABLED_PLATFORM + } + } + + // Check if we have been asked to override FIPS cryptography + fipsOverride, fipsOverrideSet := os.LookupEnv("MQ_ENABLE_FIPS") + if fipsOverrideSet { + if strings.EqualFold(fipsOverride, "false") || strings.EqualFold(fipsOverride, "0") { + FIPSEnabledType = FIPS_ENABLED_OFF + } else if strings.EqualFold(fipsOverride, "true") || strings.EqualFold(fipsOverride, "1") { + // This is the case where OS is not FIPS compliant but we have been asked to run MQ + // queue manager, web server in FIPS mode. This case can be used when running docker tests. + FIPSEnabledType = FIPS_ENABLED_ENV_VAR + } else if strings.EqualFold(fipsOverride, "auto") { + // This is the default case. Leave it to the OS default as determine above + } else { + // We don't recognise the value specified. Log a warning and carry on. + if logs != nil { + logs.Printf("Invalid value '%s' was specified for MQ_ENABLE_FIPS. The value has been ignored.\n", fipsOverride) + } + } + } +} + +func IsFIPSEnabled() bool { + return FIPSEnabledType > FIPS_ENABLED_OFF +} diff --git a/internal/fips/fips_test.go b/internal/fips/fips_test.go new file mode 100644 index 0000000..5ab42ce --- /dev/null +++ b/internal/fips/fips_test.go @@ -0,0 +1,65 @@ +/* +© Copyright IBM Corporation 2022 + +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 keystore contains code to create and update keystores +package fips + +import ( + "fmt" + "os" + "testing" +) + +func TestEnableFIPSAuto(t *testing.T) { + ProcessFIPSType(nil) + // Test default "auto" + fipsType := IsFIPSEnabled() + if fipsType { + t.Errorf("Expected FIPS OFF but got %v\n", fipsType) + } +} + +func TestEnableFIPSTrue(t *testing.T) { + // Test MQ_ENABLE_FIPS=true + os.Setenv("MQ_ENABLE_FIPS", "true") + fmt.Println(os.Getenv("MQ_ENABLE_FIPS")) + ProcessFIPSType(nil) + fipsType := IsFIPSEnabled() + if !fipsType { + t.Errorf("Expected FIPS ON but got %v\n", fipsType) + } +} + +func TestEnableFIPSFalse(t *testing.T) { + // Test MQ_ENABLE_FIPS=false + os.Setenv("MQ_ENABLE_FIPS", "false") + ProcessFIPSType(nil) + fipsType := IsFIPSEnabled() + if fipsType { + t.Errorf("Expected FIPS OFF but got %v\n", fipsType) + } + +} + +func TestEnableFIPSInvalid(t *testing.T) { + // Test MQ_ENABLE_FIPS with invalid value + os.Setenv("MQ_ENABLE_FIPS", "falseOff") + ProcessFIPSType(nil) + fipsType := IsFIPSEnabled() + if fipsType { + t.Errorf("Expected FIPS OFF but got %v\n", fipsType) + } +} diff --git a/internal/ha/ha.go b/internal/ha/ha.go index faa97d7..adb2e6c 100644 --- a/internal/ha/ha.go +++ b/internal/ha/ha.go @@ -1,5 +1,5 @@ /* -© Copyright IBM Corporation 2020, 2021 +© Copyright IBM Corporation 2020, 2022 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package ha import ( "os" + "github.com/ibm-messaging/mq-container/internal/fips" "github.com/ibm-messaging/mq-container/internal/mqtemplate" "github.com/ibm-messaging/mq-container/internal/tls" "github.com/ibm-messaging/mq-container/pkg/logger" @@ -57,6 +58,13 @@ func ConfigureNativeHA(log *logger.Logger) error { if ok { templateMap["CipherSpec"] = cipherSpec } + + // If FIPS is enabled, then set SSLFipsRequired to Yes + if fips.IsFIPSEnabled() { + templateMap["SSLFipsRequired"] = "Yes" + } else { + templateMap["SSLFipsRequired"] = "No" + } } err := mqtemplate.ProcessTemplateFile(templateFile, file, templateMap, log) diff --git a/internal/keystore/keystore.go b/internal/keystore/keystore.go index 591e168..bb3c8d9 100644 --- a/internal/keystore/keystore.go +++ b/internal/keystore/keystore.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/ibm-messaging/mq-container/internal/command" + "github.com/ibm-messaging/mq-container/internal/fips" ) // KeyStore describes information about a keystore file @@ -34,36 +35,46 @@ type KeyStore struct { Password string keyStoreType string command string + fipsEnabled bool } // NewJKSKeyStore creates a new Java Key Store, managed by the runmqckm command func NewJKSKeyStore(filename, password string) *KeyStore { - return &KeyStore{ + keyStore := &KeyStore{ Filename: filename, Password: password, keyStoreType: "jks", command: "/opt/mqm/bin/runmqckm", + fipsEnabled: fips.IsFIPSEnabled(), } + + return keyStore } // NewCMSKeyStore creates a new MQ CMS Key Store, managed by the runmqakm command func NewCMSKeyStore(filename, password string) *KeyStore { - return &KeyStore{ + keyStore := &KeyStore{ Filename: filename, Password: password, keyStoreType: "cms", command: "/opt/mqm/bin/runmqakm", + fipsEnabled: fips.IsFIPSEnabled(), } + + return keyStore } // NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command func NewPKCS12KeyStore(filename, password string) *KeyStore { - return &KeyStore{ + keyStore := &KeyStore{ Filename: filename, Password: password, keyStoreType: "p12", command: "/opt/mqm/bin/runmqakm", + fipsEnabled: fips.IsFIPSEnabled(), } + + return keyStore } // Create a key store, if it doesn't already exist @@ -100,7 +111,7 @@ func (ks *KeyStore) Create() error { } // Create the keystore now we're sure it doesn't exist - out, _, err := command.Run(ks.command, "-keydb", "-create", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password, "-stash") + out, _, err := command.Run(ks.command, "-keydb", "-create", ks.getFipsEnabledFlag(), "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password, "-stash") if err != nil { return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out) } @@ -115,7 +126,7 @@ func (ks *KeyStore) CreateStash() error { _, err := os.Stat(stashFile) if err != nil { if os.IsNotExist(err) { - out, _, err := command.Run(ks.command, "-keydb", "-stashpw", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) + out, _, err := command.Run(ks.command, "-keydb", ks.getFipsEnabledFlag(), "-stashpw", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) if err != nil { return fmt.Errorf("error running \"%v -keydb -stashpw\": %v %s", ks.command, err, out) } @@ -127,7 +138,7 @@ func (ks *KeyStore) CreateStash() error { // Import imports a certificate file in the keystore func (ks *KeyStore) Import(inputFile, password string) error { - out, _, err := command.Run(ks.command, "-cert", "-import", "-file", inputFile, "-pw", password, "-target", ks.Filename, "-target_pw", ks.Password, "-target_type", ks.keyStoreType) + out, _, err := command.Run(ks.command, "-cert", "-import", ks.getFipsEnabledFlag(), "-file", inputFile, "-pw", password, "-target", ks.Filename, "-target_pw", ks.Password, "-target_type", ks.keyStoreType) if err != nil { return fmt.Errorf("error running \"%v -cert -import\": %v %s", ks.command, err, out) } @@ -136,7 +147,7 @@ func (ks *KeyStore) Import(inputFile, password string) error { // CreateSelfSignedCertificate creates a self-signed certificate in the keystore func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) error { - out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn, "-san_dnsname", hostname, "-size 2048 -sig_alg sha256 -eku serverAuth") + out, _, err := command.Run(ks.command, "-cert", "-create", ks.getFipsEnabledFlag(), "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn, "-san_dnsname", hostname, "-size 2048 -sig_alg sha256 -eku serverAuth") if err != nil { return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out) } @@ -145,7 +156,7 @@ func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) erro // Add adds a CA certificate to the keystore func (ks *KeyStore) Add(inputFile, label string) error { - out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label) + out, _, err := command.Run(ks.command, "-cert", "-add", ks.getFipsEnabledFlag(), "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label) if err != nil { return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out) } @@ -154,7 +165,7 @@ func (ks *KeyStore) Add(inputFile, label string) error { // Add adds a CA certificate to the keystore func (ks *KeyStore) AddNoLabel(inputFile string) error { - out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile) + out, _, err := command.Run(ks.command, "-cert", "-add", ks.getFipsEnabledFlag(), "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile) if err != nil { return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out) } @@ -163,7 +174,7 @@ func (ks *KeyStore) AddNoLabel(inputFile string) error { // GetCertificateLabels returns the labels of all certificates in the key store func (ks *KeyStore) GetCertificateLabels() ([]string, error) { - out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) + out, _, err := command.Run(ks.command, "-cert", "-list", ks.getFipsEnabledFlag(), "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) if err != nil { return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out) } @@ -207,7 +218,7 @@ func (ks *KeyStore) RenameCertificate(from, to string) error { // ListAllCertificates Lists all certificates in the keystore func (ks *KeyStore) ListAllCertificates() ([]string, error) { - out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) + out, _, err := command.Run(ks.command, "-cert", "-list", ks.getFipsEnabledFlag(), "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) if err != nil { return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out) } @@ -226,3 +237,22 @@ func (ks *KeyStore) ListAllCertificates() ([]string, error) { } return labels, nil } + +// Returns the FIPS flag. True if enabled else false +func (ks *KeyStore) IsFIPSEnabled() bool { + return ks.fipsEnabled +} + +// Returns -fips option if FIPS is enabled otherwise empty string. Return value is used +// when running runmqakm/runmqckm commands. +func (ks *KeyStore) getFipsEnabledFlag() string { + var fipsEnabled string + + if ks.fipsEnabled { + fipsEnabled = "-fips" + } else { + fipsEnabled = "" + } + + return fipsEnabled +} diff --git a/internal/tls/tls.go b/internal/tls/tls.go index 32fb105..cd4f77b 100644 --- a/internal/tls/tls.go +++ b/internal/tls/tls.go @@ -118,18 +118,25 @@ func ConfigureTLS(keyLabel string, cmsKeystore KeyStoreData, devMode bool, log * const mqsc string = "/etc/mqm/15-tls.mqsc" const mqscTemplate string = mqsc + ".tpl" sslKeyRing := "" + var fipsEnabled = "NO" // 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 { + // Key label will be blank if no private keys were added during processing keys and certs. + if cmsKeystore.Keystore != nil && len(keyLabel) > 0 { certList, _ := cmsKeystore.Keystore.ListAllCertificates() if len(certList) > 0 { sslKeyRing = strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb") } + + if cmsKeystore.Keystore.IsFIPSEnabled() { + fipsEnabled = "YES" + } } + err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{ "SSLKeyR": sslKeyRing, "CertificateLabel": keyLabel, + "SSLFips": fipsEnabled, }, log) if err != nil { return err @@ -631,7 +638,7 @@ func haveKeysAndCerts(keyDir string) bool { // 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") { + if strings.HasSuffix(key.Name(), ".key") || strings.HasSuffix(key.Name(), ".crt") { // We found at least one key/crt file. return true } diff --git a/internal/tls/tls_web.go b/internal/tls/tls_web.go index 3789114..0ced4a8 100644 --- a/internal/tls/tls_web.go +++ b/internal/tls/tls_web.go @@ -77,7 +77,6 @@ func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (strin if err != nil { return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystoreFile, genHostName, err) } - } else { // Check Web Keystore already exists _, err := os.Stat(webKeystoreFile) diff --git a/test/docker/devconfig_test.go b/test/docker/devconfig_test.go index 27d7589..e057d8a 100644 --- a/test/docker/devconfig_test.go +++ b/test/docker/devconfig_test.go @@ -24,6 +24,7 @@ import ( "strings" "testing" "time" + "crypto/tls" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" @@ -59,10 +60,10 @@ func TestDevGoldenPath(t *testing.T) { runJMSTests(t, cli, id, false, "app", defaultAppPasswordOS, "false", "") }) t.Run("REST admin", func(t *testing.T) { - testRESTAdmin(t, cli, id, insecureTLSConfig) + testRESTAdmin(t, cli, id, insecureTLSConfig, "") }) t.Run("REST messaging", func(t *testing.T) { - testRESTMessaging(t, cli, id, insecureTLSConfig, qm, "app", defaultAppPasswordWeb) + testRESTMessaging(t, cli, id, insecureTLSConfig, qm, "app", defaultAppPasswordWeb, "") }) // Stop the container cleanly stopContainer(t, cli, id) @@ -124,10 +125,10 @@ func TestDevSecure(t *testing.T) { runJMSTests(t, cli, ctr.ID, true, "app", appPassword, "false", "TLS_RSA_WITH_AES_256_CBC_SHA256") }) t.Run("REST admin", func(t *testing.T) { - testRESTAdmin(t, cli, ctr.ID, insecureTLSConfig) + testRESTAdmin(t, cli, ctr.ID, insecureTLSConfig, "") }) t.Run("REST messaging", func(t *testing.T) { - testRESTMessaging(t, cli, ctr.ID, insecureTLSConfig, qm, "app", appPassword) + testRESTMessaging(t, cli, ctr.ID, insecureTLSConfig, qm, "app", appPassword, "") }) // Stop the container cleanly @@ -343,6 +344,7 @@ func TestSSLKEYRWithCACert(t *testing.T) { // 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)") { // Although queue manager is ready, it may be that MQSC scripts have not been applied yet. // Hence wait for a second and retry few times before giving up. @@ -371,3 +373,412 @@ func TestSSLKEYRWithCACert(t *testing.T) { // Stop the container cleanly stopContainer(t, cli, ctr.ID) } + +// Verifies SSLFIPS is set to NO if MQ_ENABLE_FIPS=false +func TestSSLFIPSNO(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", + "MQ_ENABLE_FIPS=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, SSLFIPS and CERTLABL attibutes. + // Search the console output for exepcted values + _, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"}) + if !strings.Contains(sslFIPSOutput, "SSLKEYR(/run/runmqserver/tls/key)") { + t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslFIPSOutput) + } + if !strings.Contains(sslFIPSOutput, "CERTLABL(default)") { + t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", sslFIPSOutput) + } + + if !strings.Contains(sslFIPSOutput, "SSLFIPS(NO)") { + t.Errorf("Expected SSLFIPS to be 'NO' but it is not; got \"%v\"", sslFIPSOutput) + } + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} + +// Verifies SSLFIPS is set to YES if certificates for queue manager +// are supplied and MQ_ENABLE_FIPS=true +func TestSSLFIPSYES(t *testing.T) { + t.Parallel() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + appPassword := "differentPassw0rd" + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_APP_PASSWORD=" + appPassword, + "MQ_QMGR_NAME=QM1", + "MQ_ENABLE_EMBEDDED_WEB_SERVER=false", + "MQ_ENABLE_FIPS=true", + }, + 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) + + // Check for expected message on container log + logs := inspectLogs(t, cli, ctr.ID) + if !strings.Contains(logs, "FIPS cryptography is enabled.") { + t.Errorf("Expected 'FIPS cryptography is enabled.' but got %v\n", logs) + } + + // execute runmqsc to display qmgr SSLKEYR, SSLFIPS and CERTLABL attibutes. + // Search the console output for exepcted values + _, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"}) + if !strings.Contains(sslFIPSOutput, "SSLKEYR(/run/runmqserver/tls/key)") { + t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslFIPSOutput) + } + if !strings.Contains(sslFIPSOutput, "CERTLABL(default)") { + t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", sslFIPSOutput) + } + + if !strings.Contains(sslFIPSOutput, "SSLFIPS(YES)") { + t.Errorf("Expected SSLFIPS to be 'YES' but it is not; got \"%v\"", sslFIPSOutput) + } + + t.Run("JMS", func(t *testing.T) { + // Run the JMS tests, with no password specified + runJMSTests(t, cli, ctr.ID, true, "app", appPassword, "false", "TLS_RSA_WITH_AES_256_CBC_SHA256") + }) + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} + +// TestDevSecureFIPSYESWeb verifies if the MQ Web Server is running in FIPS mode +func TestDevSecureFIPSTrueWeb(t *testing.T) { + t.Parallel() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + const tlsPassPhrase string = "passw0rd" + qm := "qm1" + appPassword := "differentPassw0rd" + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_QMGR_NAME=" + qm, + "MQ_APP_PASSWORD=" + appPassword, + "DEBUG=1", + "WLP_LOGGING_MESSAGE_FORMAT=JSON", + "MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG=true", + "MQ_ENABLE_FIPS=true", + }, + Image: imageName(), + } + hostConfig := container.HostConfig{ + Binds: []string{ + coverageBind(t), + tlsDir(t, false) + ":/etc/mqm/pki/keys/default", + tlsDir(t, false) + ":/etc/mqm/pki/trust/default", + }, + // Assign a random port for the web server on the host + // TODO: Don't do this for all tests + PortBindings: nat.PortMap{ + "9443/tcp": []nat.PortBinding{ + { + HostIP: "0.0.0.0", + }, + }, + }, + } + 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) + cert := filepath.Join(tlsDir(t, true), "server.crt") + waitForWebReady(t, cli, ctr.ID, createTLSConfig(t, cert, tlsPassPhrase)) + + // Create a TLS Config with a cipher to use when connecting over HTTPS + var secureTLSConfig *tls.Config = createTLSConfigWithCipher(t, cert, tlsPassPhrase, []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384}) + // Put a message to queue + t.Run("REST messaging", func(t *testing.T) { + testRESTMessaging(t, cli, ctr.ID, secureTLSConfig, qm, "app", appPassword, "") + }) + + // Create a TLS Config with a non-FIPS cipher to use when connecting over HTTPS + var secureNonFIPSCipherConfig *tls.Config = createTLSConfigWithCipher(t, cert, tlsPassPhrase, []uint16{tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}) + // Put a message to queue - the attempt to put message will fail with a EOF return message. + t.Run("REST messaging", func(t *testing.T) { + testRESTMessaging(t, cli, ctr.ID, secureNonFIPSCipherConfig, qm, "app", appPassword, "EOF") + }) + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} + +// TestDevSecureNOFIPSWeb verifies if the MQ Web Server is not running in FIPS mode +func TestDevSecureFalseFIPSWeb(t *testing.T) { + t.Parallel() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + const tlsPassPhrase string = "passw0rd" + qm := "qm1" + appPassword := "differentPassw0rd" + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_QMGR_NAME=" + qm, + "MQ_APP_PASSWORD=" + appPassword, + "DEBUG=1", + "WLP_LOGGING_MESSAGE_FORMAT=JSON", + "MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG=true", + "MQ_ENABLE_FIPS=false", + }, + Image: imageName(), + } + hostConfig := container.HostConfig{ + Binds: []string{ + coverageBind(t), + tlsDir(t, false) + ":/etc/mqm/pki/keys/default", + tlsDir(t, false) + ":/etc/mqm/pki/trust/default", + }, + // Assign a random port for the web server on the host + PortBindings: nat.PortMap{ + "9443/tcp": []nat.PortBinding{ + { + HostIP: "0.0.0.0", + }, + }, + }, + } + 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) + + cert := filepath.Join(tlsDir(t, true), "server.crt") + waitForWebReady(t, cli, ctr.ID, createTLSConfig(t, cert, tlsPassPhrase)) + + // As FIPS is not enabled, the MQ WebServer (actually Java) will choose a JSSE provider from the list + // specified in java.security file. We will need to enable java.net.debug and then parse the web server + // logs to check what JJSE provider is being used. Hence just check the jvm.options file does not contain + // -Dcom.ibm.jsse2.usefipsprovider line. + _, jvmOptionsOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "cat /var/mqm/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options"}) + if strings.Contains(jvmOptionsOutput, "-Dcom.ibm.jsse2.usefipsprovider") { + t.Errorf("Did not expect -Dcom.ibm.jsse2.usefipsprovider but it is not; got \"%v\"", jvmOptionsOutput) + } + + // Just do a HTTPS GET as well to query installation details. + var secureTLSConfig *tls.Config = createTLSConfigWithCipher(t, cert, tlsPassPhrase, []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384}) + t.Run("REST admin", func(t *testing.T) { + testRESTAdmin(t, cli, ctr.ID, secureTLSConfig, "") + }) + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} + +// Verify SSLFIPS is set to NO if no certificates were supplied +func TestSSLFIPSTrueNoCerts(t *testing.T) { + t.Parallel() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + appPassword := "differentPassw0rd" + containerConfig := container.Config{ + Env: []string{ + "LICENSE=accept", + "MQ_APP_PASSWORD=" + appPassword, + "MQ_QMGR_NAME=QM1", + "MQ_ENABLE_EMBEDDED_WEB_SERVER=false", + "MQ_ENABLE_FIPS=true", + }, + Image: imageName(), + } + hostConfig := container.HostConfig{ + Binds: []string{ + coverageBind(t), + }, + } + 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, SSLFIPS and CERTLABL attibutes. + // Search the console output for exepcted values + _, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"}) + if !strings.Contains(sslFIPSOutput, "SSLKEYR( )") { + t.Errorf("Expected SSLKEYR to be ' ' but it is not; got \"%v\"", sslFIPSOutput) + } + if !strings.Contains(sslFIPSOutput, "CERTLABL( )") { + t.Errorf("Expected CERTLABL to be blank but it is not; got \"%v\"", sslFIPSOutput) + } + + if !strings.Contains(sslFIPSOutput, "SSLFIPS(NO)") { + t.Errorf("Expected SSLFIPS to be 'NO' but it is not; got \"%v\"", sslFIPSOutput) + } + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} + +// Verifies SSLFIPS is set to NO if MQ_ENABLE_FIPS=tru (invalid value) +func TestSSLFIPSInvalidValue(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", + "MQ_ENABLE_FIPS=tru", + }, + 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, SSLFIPS and CERTLABL attibutes. + // Search the console output for exepcted values + _, sslFIPSOutput := execContainer(t, cli, ctr.ID, "", []string{"bash", "-c", "echo 'DISPLAY QMGR SSLKEYR CERTLABL SSLFIPS' | runmqsc"}) + if !strings.Contains(sslFIPSOutput, "SSLKEYR(/run/runmqserver/tls/key)") { + t.Errorf("Expected SSLKEYR to be '/run/runmqserver/tls/key' but it is not; got \"%v\"", sslFIPSOutput) + } + + if !strings.Contains(sslFIPSOutput, "CERTLABL(default)") { + t.Errorf("Expected CERTLABL to be 'default' but it is not; got \"%v\"", sslFIPSOutput) + } + + if !strings.Contains(sslFIPSOutput, "SSLFIPS(NO)") { + t.Errorf("Expected SSLFIPS to be 'NO' but it is not; got \"%v\"", sslFIPSOutput) + } + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} + +// Container creation fails when invalid certs are passed and MQ_ENABLE_FIPS set true +func TestSSLFIPSBadCerts(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", + "MQ_ENABLE_FIPS=true", + }, + Image: imageName(), + } + hostConfig := container.HostConfig{ + Binds: []string{ + coverageBind(t), + tlsDirInvalid(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) + + rc := waitForContainer(t, cli, ctr.ID, 20*time.Second) + // Expect return code 1 if container failed to create. + if rc == 1 { + // Get container logs and search for specific message. + logs := inspectLogs(t, cli, ctr.ID) + if strings.Contains(logs, "Failed to parse private key") { + t.Logf("Container creating failed because of invalid certifates") + } + } else { + // Some other error occurred. + t.Errorf("Expected rc=0, got rc=%v", rc) + } + + // Stop the container cleanly + stopContainer(t, cli, ctr.ID) +} diff --git a/test/docker/devconfig_test_util.go b/test/docker/devconfig_test_util.go index f61ddf5..494a164 100644 --- a/test/docker/devconfig_test_util.go +++ b/test/docker/devconfig_test_util.go @@ -86,6 +86,10 @@ func tlsDirWithCA(t *testing.T, unixPath bool) string { return filepath.Join(getCwd(t, unixPath), "../tlscacert") } +func tlsDirInvalid(t *testing.T, unixPath bool) string { + return filepath.Join(getCwd(t, unixPath), "../tlsinvalidcert") +} + // runJMSTests runs a container with a JMS client, which connects to the queue manager container with the specified ID func runJMSTests(t *testing.T, cli *client.Client, ID string, tls bool, user, password string, ibmjre string, cipherName string) { containerConfig := container.Config{ @@ -201,7 +205,7 @@ func createTLSConfig(t *testing.T, certFile, password string) *tls.Config { } } -func testRESTAdmin(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config) { +func testRESTAdmin(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config, errorExpected string) { httpClient := http.Client{ Timeout: time.Duration(30 * time.Second), Transport: &http.Transport{ @@ -213,9 +217,15 @@ func testRESTAdmin(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.C req.SetBasicAuth("admin", defaultAdminPassword) resp, err := httpClient.Do(req) if err != nil { - t.Fatal(err) + if len(errorExpected) > 0 { + if !strings.Contains(err.Error(), errorExpected) { + t.Fatal(err) + } + } else { + t.Fatal(err) + } } - if resp.StatusCode != http.StatusOK { + if resp != nil && resp.StatusCode != http.StatusOK { t.Errorf("Expected HTTP status code %v from 'GET installation'; got %v", http.StatusOK, resp.StatusCode) } } @@ -238,7 +248,7 @@ func logHTTPResponse(t *testing.T, resp *http.Response) { t.Logf("HTTP response: %v", string(d)) } -func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config, qmName string, user string, password string) { +func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *tls.Config, qmName string, user string, password string, errorExpected string) { httpClient := http.Client{ Timeout: time.Duration(30 * time.Second), Transport: &http.Transport{ @@ -255,10 +265,19 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t logHTTPRequest(t, req) resp, err := httpClient.Do(req) if err != nil { - t.Fatal(err) + if len(errorExpected) > 0 { + if strings.Contains(err.Error(), errorExpected) { + t.Logf("Error contains expected '%s' value", errorExpected) + return + } else { + t.Fatal(err) + } + } else { + t.Fatal(err) + } } logHTTPResponse(t, resp) - if resp.StatusCode != http.StatusCreated { + if resp != nil && resp.StatusCode != http.StatusCreated { t.Errorf("Expected HTTP status code %v from 'POST to queue'; got %v", http.StatusOK, resp.StatusCode) t.Logf("HTTP response: %+v", resp) t.Fail() @@ -286,3 +305,28 @@ func testRESTMessaging(t *testing.T, cli *client.Client, ID string, tlsConfig *t t.Errorf("Expected payload to be \"%s\"; got \"%s\"", putMessage, gotMessage) } } + +// createTLSConfig creates a tls.Config which trusts the specified certificate +func createTLSConfigWithCipher(t *testing.T, certFile, password string, ciphers []uint16) *tls.Config { + // Get the SystemCertPool, continue with an empty pool on error + certs, err := x509.SystemCertPool() + if err != nil { + t.Fatal(err) + } + // Read in the cert file + cert, err := ioutil.ReadFile(certFile) + if err != nil { + t.Fatal(err) + } + // Append our cert to the system pool + ok := certs.AppendCertsFromPEM(cert) + if !ok { + t.Fatal("No certs appended") + } + // Trust the augmented cert pool in our client + return &tls.Config{ + InsecureSkipVerify: false, + RootCAs: certs, + CipherSuites: ciphers, + } +} diff --git a/test/docker/docker_api_test.go b/test/docker/docker_api_test.go index 40d8b2a..73aeb97 100644 --- a/test/docker/docker_api_test.go +++ b/test/docker/docker_api_test.go @@ -52,8 +52,8 @@ func TestLicenseNotSet(t *testing.T) { expectTerminationMessage(t, cli, id) } -//Start container with LICENSE environment variable set to view. -//Check that container starts and display license text +// Start container with LICENSE environment variable set to view. +// Check that container starts and display license text func TestLicenseView(t *testing.T) { t.Parallel() @@ -77,8 +77,8 @@ func TestLicenseView(t *testing.T) { } } -//Start a container with qm grace set to x seconds -//Check that when the container is stopped that the command endmqm has option -tp and x +// Start a container with qm grace set to x seconds +// Check that when the container is stopped that the command endmqm has option -tp and x func TestEndMQMOpts(t *testing.T) { t.Parallel() cli, err := client.NewClientWithOpts(client.FromEnv) @@ -134,7 +134,6 @@ func goldenPath(t *testing.T, metric bool) { stopContainer(t, cli, id) } - func utilTestNoQueueManagerName(t *testing.T, hostName string, expectedName string) { search := "QMNAME(" + expectedName + ")" cli, err := client.NewClientWithOpts(client.FromEnv) diff --git a/test/docker/docker_api_test_util.go b/test/docker/docker_api_test_util.go index e31d8d2..250c32b 100644 --- a/test/docker/docker_api_test_util.go +++ b/test/docker/docker_api_test_util.go @@ -889,3 +889,42 @@ func getMQVersion(t *testing.T, cli *client.Client) (string, error) { version := inspect.ContainerConfig.Labels["version"] return version, nil } + +// runContainerWithAllConfig creates and starts a container, using the supplied ContainerConfig, HostConfig, +// NetworkingConfig, and container name (or the value of t.Name if containerName=""). +func runContainerWithAllConfigError(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (string, error) { + if containerName == "" { + containerName = t.Name() + } + if containerConfig.Image == "" { + containerConfig.Image = imageName() + } + // Always run as a random user, unless the test has specified otherwise + if containerConfig.User == "" { + containerConfig.User = generateRandomUID() + } + // if coverage + containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov") + containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t)) + t.Logf("Running container (%s)", containerConfig.Image) + ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, networkingConfig, containerName) + if err != nil { + return "", err + } + err = startContainerError(t, cli, ctr.ID) + if err != nil { + return "", err + } + return ctr.ID, nil +} + +func startContainerError(t *testing.T, cli *client.Client, ID string) error { + t.Logf("Starting container: %v", ID) + startOptions := types.ContainerStartOptions{} + err := cli.ContainerStart(context.Background(), ID, startOptions) + if err != nil { + return err + } + + return nil +} diff --git a/test/docker/mq_native_ha_test.go b/test/docker/mq_native_ha_test.go index 6f2f0d5..dc88756 100644 --- a/test/docker/mq_native_ha_test.go +++ b/test/docker/mq_native_ha_test.go @@ -16,9 +16,9 @@ limitations under the License. package main import ( - "testing" - "github.com/docker/docker/client" + "strings" + "testing" ) // TestNativeHABasic creates 3 containers in a Native HA queue manager configuration @@ -217,3 +217,96 @@ func TestNativeHASecureCipherSpec(t *testing.T) { } } + +// TestNativeHASecure creates 3 containers in a Native HA queue manager configuration +// with HA TLS FIPS enabled, overrides the default CipherSpec, and ensures the queue manger +// and replicas start as expected. This test uses FIPS compliant cipher. +func TestNativeHASecureCipherSpecFIPS(t *testing.T) { + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + version, err := getMQVersion(t, cli) + if err != nil { + t.Fatal(err) + } + if version < "9.2.2.0" { + t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version) + } + + containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"} + qmReplicaIDs := [3]string{} + qmNetwork, err := createBridgeNetwork(cli, t) + if err != nil { + t.Fatal(err) + } + defer removeBridgeNetwork(cli, qmNetwork.ID) + + for i := 0; i <= 2; i++ { + containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort) + // MQ_NATIVE_HA_CIPHERSPEC is set a FIPS compliant cipherspec. + containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_RSA_WITH_AES_128_GCM_SHA256", "MQ_ENABLE_FIPS=true") + hostConfig := getNativeHASecureHostConfig(t) + networkingConfig := getNativeHANetworkConfig(qmNetwork.ID) + + ctr := runContainerWithAllConfig(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i]) + defer cleanContainer(t, cli, ctr) + qmReplicaIDs[i] = ctr + } + + waitForReadyHA(t, cli, qmReplicaIDs) + // Display the contents of qm.ini + _, qmini := execContainer(t, cli, qmReplicaIDs[0], "", []string{"cat", "/var/mqm/qmgrs/QM1/qm.ini"}) + if !strings.Contains(qmini, "SSLFipsRequired=Yes") { + t.Errorf("Expected SSLFipsRequired=Yes but it is not; got \"%v\"", qmini) + } + + _, err = getActiveReplicaInstances(t, cli, qmReplicaIDs) + if err != nil { + t.Fatal(err) + } +} + +// TestNativeHASecure creates 3 containers in a Native HA queue manager configuration +// with HA TLS FIPS enabled with non-FIPS cipher, overrides the default CipherSpec, and +// ensures the queue manger and replicas don't start as expected +func TestNativeHASecureCipherSpecNonFIPSCipher(t *testing.T) { + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + t.Fatal(err) + } + + version, err := getMQVersion(t, cli) + if err != nil { + t.Fatal(err) + } + if version < "9.2.2.0" { + t.Skipf("Skipping %s as test requires at least MQ 9.2.2.0, but image is version %s", t.Name(), version) + } + + containerNames := [3]string{"QM1_1", "QM1_2", "QM1_3"} + qmReplicaIDs := [3]string{} + qmNetwork, err := createBridgeNetwork(cli, t) + if err != nil { + t.Fatal(err) + } + defer removeBridgeNetwork(cli, qmNetwork.ID) + + for i := 0; i <= 2; i++ { + containerConfig := getNativeHAContainerConfig(containerNames[i], containerNames, defaultHAPort) + // MQ_NATIVE_HA_CIPHERSPEC is set a FIPS non-compliant cipherspec - SSL_ECDHE_ECDSA_WITH_RC4_128_SHA + containerConfig.Env = append(containerConfig.Env, "MQ_NATIVE_HA_TLS=true", "MQ_NATIVE_HA_CIPHERSPEC=TLS_RSA_WITH_AES_128_GCM_SHA256", "MQ_ENABLE_FIPS=true") + hostConfig := getNativeHASecureHostConfig(t) + networkingConfig := getNativeHANetworkConfig(qmNetwork.ID) + + ctr, err := runContainerWithAllConfigError(t, cli, &containerConfig, &hostConfig, &networkingConfig, containerNames[i]) + defer cleanContainer(t, cli, ctr) + // We expect container to fail in this case because the cipher is non-FIPS and we have asked for FIPS compliance + // by setting MQ_ENABLE_FIPS=true + if err == nil { + t.Logf("Container start expected to fail but did not. %v", err) + } + qmReplicaIDs[i] = ctr + } +} diff --git a/test/docker/mq_native_ha_test_util.go b/test/docker/mq_native_ha_test_util.go index a94c68d..aa24054 100644 --- a/test/docker/mq_native_ha_test_util.go +++ b/test/docker/mq_native_ha_test_util.go @@ -106,7 +106,7 @@ func getActiveReplicaInstances(t *testing.T, cli *client.Client, qmReplicaIDs [3 func waitForReadyHA(t *testing.T, cli *client.Client, qmReplicaIDs [3]string) { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute) defer cancel() for { diff --git a/test/tlsinvalidcert/expired.key b/test/tlsinvalidcert/expired.key new file mode 100644 index 0000000..dff0f5e --- /dev/null +++ b/test/tlsinvalidcert/expired.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIxuHuzG+WcZACAggA +MBQGCCqGSIb3DQMHBAgMKyhajZd/ewSCBMiCL57/BUvVwtxmhaIqQBSjHLnx+AvG +f+frFkwM52KuAg0Q+vF/1iQXMbZRrHpxNz/1t3cIdwzlc9S3FB+L42q0CEH0c1FW +6gB6csmb1AyhGYffwkeT8bPkELJhpO2taIFTynwDhHaeKeDce3EwQ5xQd4ydq1Ku +t4oJZun51J5qvNs91BiHHVB3/q1Lnh4VK4TTDgXTGfu8qcuOexHQcOgko4oemorv +E1Woy6Lf99bePxedSuMIxPAXysSCBqcrZDbY9K3yaxV6Gr7S9FBVvOx9r8PTaFEY +sXxdtB22qM1PqZIIjK6nzwXb5iu8wcaZFatD6y7RQaS0kz5EcS5kExrEocFV7D1T +dtTf6tt31PbtKfEauRjz3fBXMw+r3Pu/hWRXd3og9hfzFQz49v9RdvgkHy57uCY+ +F/fN3BKyTcTqJWKpBDMx/xtD1kXWHf81SFmckuShbgKjDvZraR4RmzO/9upIDCFF +rJ85takmPvZyAdfvZp6RqVja+cphI4nmxVvmOmUiFMmUJ0lv9MhqpxdVFfleWz0w +9XxeL4l+cd/Kbl2ihHwF4oOzWlamyRZjoWiq5s/ika9I1iQ40rGTThqnphonlhUz +HX4NvA+fbugwqyOjqEkg5AqyP5ucUN3Hl0qd7wqAJprDkB938unKo/1sr82PfdO/ +5UvzsKz2ieVavEzi/NxYyR+Xjm3YHG1akcftvElcpYepvemt/PtXJH8fX29b1X6C +TYxV03MeA1sJXeM9iqIf/wGQJ657U5ciXUyUtU8s0dDY+jqpZVjxekgCove9FNXz +Mja15yiAy/F8xKX20ZRisg+Ly60UQYbX6Wu6Uy6NMjWkxZOYebCVYLMag6UjS0CS +8n3/DNvuVooGWbgBFs0tyabtlz/Fz5jGktkMBQeEK4PZKZdMt9aXnDfPFGJWrsme +bUzEZLZSWD2Zwr/ujzfBgVeg56AyHJzHuOOLaofZ90ecv69Udl7sYZBelFXxgN8R +JVXyV0kNZYj9LaqRTi54H8YToAIV7GXIcasH3jP54dOba3px2NXbaiMf7Oe8apyW +E7/vKQNoIbWaglRHXVPGoATTUzYNdBO4jca1rTOXjmzsiZl1JGPwQsS03PuTfa8C +F7Uqxj8P9m71G1KuLYA1es55aNXzXSx6uqrRYeu0iug/jE+voGfjbRW3BUY/qxtl +OjR8pCDPLN6/rt//ejvBdA4gPDgjRNH4fO9DULQDqIVHmYlysZqhbPbhzmiBXZVP +zw1/z9amWR00OeN7xUvm9n+65dosoIn2v1dOz8JBh6Uooj63D2cOghBwEXoPg53B +3vLgqy3NKyNZQ/gGfBjXTTeoWzAy8U8hKecNKcsTBquBeBUfS55WwkbD7Mz0Deho +EcMZPZUwAPyJ2kHL74etpoBXuH7Jzo8SVedhE+C5J6F+oUP/JcoXdehdGLI89CHU +cUARt91OiF3WQo51xT21GlGL8RMHVpjTnYZinnQ/fE1y7JfWtYshBya+YLlAk5vb +9jt0tZ9I/KhJ579ZwWBH8V179kBr5Vn+uWlEBBaJu0abdbRSDjt6fAaxi3Q8XK+c +isuVuzN/8JAeRgRpm7hRfIoG6XOsrY72ktz0JiHflZ+90xaPRvpCA9/mFC5qp1zh +yBs= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test/tlsinvalidcert/expired.p12 b/test/tlsinvalidcert/expired.p12 new file mode 100644 index 0000000000000000000000000000000000000000..23dcb789db4d3d5bcbb79990073b68779eedfc2d GIT binary patch literal 2733 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 GxAtG{!ypI% literal 0 HcmV?d00001 diff --git a/test/tlsinvalidcert/expiredCA.pem b/test/tlsinvalidcert/expiredCA.pem new file mode 100644 index 0000000..fa4167e --- /dev/null +++ b/test/tlsinvalidcert/expiredCA.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGEzCCA/ugAwIBAgIJAI+sAC+/ups4MA0GCSqGSIb3DQEBBQUAMIGfMQswCQYD +VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxDzANBgNVBAcMBkFybW9uazE0MDIG +A1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlv +bjE2MDQGA1UEAwwtTVEgTGlnaHQgRXhwaXJlZCBDQSAoZm9yIGV4cGlyZWQgc2ln +bmVkIGNlcnQpMB4XDTE1MDQwMTExNTkxNloXDTQyMDgxNjExNTkxNlowgZ8xCzAJ +BgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMTQw +MgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0 +aW9uMTYwNAYDVQQDDC1NUSBMaWdodCBFeHBpcmVkIENBIChmb3IgZXhwaXJlZCBz +aWduZWQgY2VydCkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLGn4P +97BZteNO/5zIlGH4F067TPlOjFiLI1TFI/bKp3bx1fHpsQdMX5C9y1s6BqoOzMJV +VHDJH4NlxvnMUGNzYvluynjujejhyDryDrb4uY3JMcewvnHmrWArzbfZw4eBzANr +feBkro4xr6m5HWMxM4GJZeFaEGqbXNE5vAYhZ270qUKqrkSHv4g/LqapITaBPJDC +3tAtV0UD4/HmwB2ogZlXy4DkbBsgcc2rS4xPxx3Qki78DeA9oT6aeLP7KZnoEgo9 +Xqb/UfRuZ5MYR17u8yTuJz/JdAs6Saf80t5PLDO/SRKj7Uu1lt+HbkpEghdEdB/V +pPdmlIfo7uNe7rFG/OWif2qwVm2t9o02oIUqlAHggS4WYeOmcB8L9EtmlfZHaoDA +H0a0xlbZ+XlE/EMpphgrzSE5g9h9Vw7vvH5Ygzks8lSXKd5VS8C9y8tWqaMCynAy +5MTIZdXMvouG8vy5Ip+iWck4QSo6eJBryLNt/vzljxo6XntveXRZVvywyjh3NuVu +nVLPe0CFkmRHXK5/zA6H8rZeGb3eK8Y4VcLKj7x4Lnluo47BeGnKH7UcKBA2AREC +VGFVsHcr0HwKgCCSsyv4qRefY/mJWO59BL38shHuzydLxlKHf/uPGdsZ3DU0rb6i +QXMCHC6lzAjJHVCDJ2witiPSjmW2LRZzRqdTqwIDAQABo1AwTjAdBgNVHQ4EFgQU +dv3XdSdsOkZJDWgusfuZqBx/jpMwHwYDVR0jBBgwFoAUdv3XdSdsOkZJDWgusfuZ +qBx/jpMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAR9o32FR4AjTZ +LV5nH5rsQ9OVon1TCv9yemAI4coh08RHWVf7cDyIp8XKy3gTM/lq70wzsXUHwaAS +4xSOjc/fBLP0UTEwCrdyy1guRs9ZjKIhqth1AkB92sF9HV4P811vUNppVfA/BDmb +Ttl6i1Joyl6nhnDxn1HzjcSBB4ZVt/1MRwIaEfRXrqAklYbMOtw8D6Onth6IQ7dS +27ulUPD+AA8H5Ilm2XhPI6ttnR+822+mgB1K9WAjmuIbDJwQJl32UmTUiXPwdlWm +KqyKZGST8wV6Jylha84ETL99IlJsxoAMGmNshMe4t773n0LousAvnbVt9uZcBNUH +d/53EWP7kw1PPhY6jo0gLVK/ZQt8MHBJuT3f5IM9dmkLJmyM9t8tEmvjNEoC2xSW +JHxoWfbMOxxFx+S5CwwcvySpKltio7s5bf/dYexE+Dv/+zMDeMb8GXv811TCqsD2 +lm4kTsHjwd+zSGCuvH+R4vWbkSrd65CATmFsVpVPLsIVXDa3DrgFQBmUI/q9UFaG +K25HztaHk8GJkBZdBA7xXxzSJxW37yqPGKqQ/ZoqSz6XFnsgy5AsW0fDM+cPUl7l +tftOlLeSqkaBq9O++76yY2asb0AyqjNs0tajluzxGFZ38DOqA1xkltV/c+KYnEv3 +q+K4agMHIKMr7lGJfSBevz3mG+NJ7AU= +-----END CERTIFICATE----- diff --git a/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options.tpl b/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options.tpl new file mode 100644 index 0000000..eb6710d --- /dev/null +++ b/web/installations/Installation1/servers/mqweb/configDropins/defaults/jvm.options.tpl @@ -0,0 +1,5 @@ +-Djava.util.prefs.userRoot=/tmp +-Djava.util.prefs.systemRoot=/tmp +-Dcom.ibm.jsse2.usefipsprovider={{.FipsProvider}} +-Dcom.ibm.jsse2.usefipsProviderName={{.FipsProviderName}} +