Refactor TLS code

This commit is contained in:
Stephen Marshall
2019-12-17 16:21:07 +00:00
committed by Stephen D Marshall
parent ce184408df
commit 956b4a8e49
9 changed files with 612 additions and 707 deletions

View File

@@ -126,19 +126,19 @@ func doMain() error {
// Print out versioning information
logVersionInfo()
keylabel, cmsDB, p12Trust, _, err := tls.ConfigureTLSKeystores(keyDir, trustDir, keyStoreDir)
keyLabel, cmsKeystore, p12Truststore, err := tls.ConfigureTLSKeystores()
if err != nil {
logTermination(err)
return err
}
err = configureTLS(keylabel, cmsDB, *devFlag)
err = tls.ConfigureTLS(keyLabel, cmsKeystore, *devFlag, log)
if err != nil {
logTermination(err)
return err
}
err = postInit(name, keylabel, p12Trust)
err = postInit(name, keyLabel, p12Truststore)
if err != nil {
logTermination(err)
return err

View File

@@ -22,23 +22,23 @@ import (
)
// postInit is run after /var/mqm is set up
func postInit(name, keylabel string, p12Trust tls.KeyStoreData) error {
func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
if enableWebServer == "true" || enableWebServer == "1" {
// Configure the web server (if enabled)
keystore, err := configureWebServer(keylabel, p12Trust)
webKeystore, err := configureWebServer(keyLabel, p12Truststore)
if err != nil {
return err
}
// If trust-store is empty, set reference to point to the key-store
p12TrustStoreRef := "MQWebTrustStore"
if len(p12Trust.TrustedCerts) == 0 {
p12TrustStoreRef = "MQWebKeyStore"
// If trust-store is empty, set reference to point to the keystore
webTruststoreRef := "MQWebTrustStore"
if len(p12Truststore.TrustedCerts) == 0 {
webTruststoreRef = "MQWebKeyStore"
}
// Start the web server, in the background (if installed)
// WARNING: No error handling or health checking available for the web server
go func() {
err = startWebServer(keystore, p12Trust.Password, p12TrustStoreRef)
err = startWebServer(webKeystore, p12Truststore.Password, webTruststoreRef)
if err != nil {
log.Printf("Error starting web server: %v", err)
}

View File

@@ -1,163 +0,0 @@
/*
© Copyright IBM Corporation 2018, 2019
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 main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/ibm-messaging/mq-container/internal/command"
"github.com/ibm-messaging/mq-container/internal/keystore"
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
"github.com/ibm-messaging/mq-container/internal/tls"
)
// Location to store the keystores
const keyStoreDir = "/run/runmqserver/tls/"
// KeyDir is the location of the certificate keys to import
const keyDir = "/etc/mqm/pki/keys"
// TrustDir is the location of the Certifates to add
const trustDir = "/etc/mqm/pki/trust"
// configureWebTLS configures TLS for Web Console
func configureWebTLS(label string) error {
// Return immediately if we have no certificate to use as identity
if label == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") == "" {
return nil
}
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
tls := "tls.xml"
tlsConfig := filepath.Join(webConfigDir, tls)
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
err := os.Remove(tlsConfig)
if err != nil {
return fmt.Errorf("Could not delete file %s: %v", tlsConfig, err)
}
// we symlink here to prevent issues on restart
err = os.Symlink(newTLSConfig, tlsConfig)
if err != nil {
return fmt.Errorf("Could not create symlink %s->%s: %v", newTLSConfig, tlsConfig, err)
}
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
return fmt.Errorf("Could not find mqm user or group: %v", err)
}
err = os.Chown(tlsConfig, mqmUID, mqmGID)
if err != nil {
return fmt.Errorf("Could change ownership of %s to mqm: %v", tlsConfig, err)
}
return nil
}
// configureTLSDev configures TLS for developer defaults
func configureTLSDev() error {
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
const mqscTemplate string = mqsc + ".tpl"
if os.Getenv("MQ_DEV") == "true" {
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{}, log)
if err != nil {
return err
}
} else {
_, err := os.Stat(mqsc)
if !os.IsNotExist(err) {
err = os.Remove(mqsc)
if err != nil {
log.Errorf("Error removing file %s: %v", mqsc, err)
return err
}
}
}
return nil
}
// configureTLS configures TLS for queue manager
func configureTLS(certLabel string, cmsKeystore tls.KeyStoreData, devmode bool) error {
log.Debug("Configuring TLS")
const mqsc string = "/etc/mqm/15-tls.mqsc"
const mqscTemplate string = mqsc + ".tpl"
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
"CertificateLabel": certLabel,
}, log)
if err != nil {
return err
}
if devmode && certLabel != "" {
err = configureTLSDev()
if err != nil {
return err
}
}
return nil
}
// configureWebKeyStore configures the key stores for the web console
func configureWebKeyStore(p12TrustStore tls.KeyStoreData) (string, error) {
// TODO find way to supply this
// Override the webstore variables to hard coded defaults
webKeyStoreName := tls.WebDefaultLabel + ".p12"
// Check keystore exists
ks := filepath.Join(keyStoreDir, webKeyStoreName)
_, err := os.Stat(ks)
// Now we know if the file exists let's check whether we should have it or not.
// Check if we're being told to generate the certificate
genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME")
if genHostName != "" {
// We've got to generate the certificate with the hostname given
if err == nil {
log.Printf("Replacing existing keystore %s - generating new certificate", ks)
}
// Keystore doesn't exist so create it and populate a certificate
newKS := keystore.NewPKCS12KeyStore(ks, p12TrustStore.Password)
err = newKS.Create()
if err != nil {
return "", fmt.Errorf("Failed to create keystore %s: %v", ks, err)
}
err = newKS.CreateSelfSignedCertificate("default", fmt.Sprintf("CN=%s", genHostName), genHostName)
if err != nil {
return "", fmt.Errorf("Failed to generate certificate in keystore %s with DN of 'CN=%s': %v", ks, genHostName, err)
}
} else {
// Keystore should already exist
if err != nil {
return "", fmt.Errorf("Failed to find existing keystore %s: %v", ks, err)
}
}
// Check truststore exists
_, err = os.Stat(p12TrustStore.Keystore.Filename)
if err != nil {
return "", fmt.Errorf("Failed to find existing truststore %s: %v", p12TrustStore.Keystore.Filename, err)
}
return webKeyStoreName, nil
}

View File

@@ -31,7 +31,7 @@ import (
"github.com/ibm-messaging/mq-container/internal/tls"
)
func startWebServer(keystore, keystorepw, p12TrustStoreRef string) error {
func startWebServer(webKeystore, webkeystorePW, webTruststoreRef string) error {
_, err := os.Stat("/opt/mqm/bin/strmqweb")
if err != nil && os.IsNotExist(err) {
log.Debug("Skipping web server, because it's not installed")
@@ -50,10 +50,10 @@ func startWebServer(keystore, keystorepw, p12TrustStoreRef string) error {
}
// TLS enabled
if keystore != "" {
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+keystore)
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keystorepw)
cmd.Env = append(cmd.Env, "AMQ_WEBTRUSTSTOREREF="+p12TrustStoreRef)
if webKeystore != "" {
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webKeystore)
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+webkeystorePW)
cmd.Env = append(cmd.Env, "AMQ_WEBTRUSTSTOREREF="+webTruststoreRef)
}
uid, gid, err := command.LookupMQM()
@@ -119,50 +119,53 @@ func configureSSO(p12TrustStore tls.KeyStoreData) (string, error) {
}
// Configure SSO TLS
return configureWebKeyStore(p12TrustStore)
return tls.ConfigureWebKeystore(p12TrustStore)
}
func configureWebServer(keyLabel string, p12Trust tls.KeyStoreData) (string, error) {
var keystore string
func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string, error) {
var webKeystore string
// Configure TLS for Web Console first if we have a certificate to use
err := configureWebTLS(keyLabel)
err := tls.ConfigureWebTLS(keyLabel)
if err != nil {
return keystore, err
return "", err
}
if keyLabel != "" {
keystore = keyLabel + ".p12"
webKeystore = keyLabel + ".p12"
}
// Configure Single-Sign-On for the web server (if enabled)
enableSSO := os.Getenv("MQ_BETA_ENABLE_SSO")
if enableSSO == "true" || enableSSO == "1" {
keystore, err = configureSSO(p12Trust)
webKeystore, err = configureSSO(p12Truststore)
if err != nil {
return keystore, err
return "", err
}
} else if keyLabel == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") != "" {
keystore, err = configureWebKeyStore(p12Trust)
webKeystore, err = tls.ConfigureWebKeystore(p12Truststore)
if err != nil {
return "", err
}
}
_, err = os.Stat("/opt/mqm/bin/strmqweb")
if err != nil {
if os.IsNotExist(err) {
return keystore, nil
return "", nil
}
return keystore, err
return "", err
}
const webConfigDir string = "/etc/mqm/web"
_, err = os.Stat(webConfigDir)
if err != nil {
if os.IsNotExist(err) {
return keystore, nil
return "", nil
}
return keystore, err
return "", err
}
uid, gid, err := command.LookupMQM()
if err != nil {
return keystore, err
return "", err
}
const prefix string = "/etc/mqm/web"
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
@@ -206,5 +209,6 @@ func configureWebServer(keyLabel string, p12Trust tls.KeyStoreData) (string, err
}
return nil
})
return keystore, err
return webKeystore, err
}

View File

@@ -9,8 +9,6 @@ The MQ Developer Defaults supports some customization options, these are all con
* **MQ_DEV** - Set this to `false` to stop the default objects being created.
* **MQ_ADMIN_PASSWORD** - Changes the password of the `admin` user. Must be at least 8 characters long.
* **MQ_APP_PASSWORD** - Changes the password of the app user. If set, this will cause the `DEV.APP.SVRCONN` channel to become secured and only allow connections that supply a valid userid and password. Must be at least 8 characters long.
* **MQ_TLS_KEYSTORE** - **DEPRECATED**. See section `Supplying TLS certificates` in [usage document](usage.md). Allows you to supply the location of a PKCS#12 keystore containing a single certificate which you want to use in both the web console and the queue manager. Requires `MQ_TLS_PASSPHRASE`. When enabled the channels created will be secured using the `TLS_RSA_WITH_AES_128_CBC_SHA256` CipherSpec. *Note*: you will need to make the keystore available inside your container, this can be done by mounting a volume to your container.
* **MQ_TLS_PASSPHRASE** - **DEPRECATED**. See section `Supplying TLS certificates` in [usage document](usage.md). Passphrase for the keystore referenced in `MQ_TLS_KEYSTORE`.
## Details of the default configuration
@@ -45,6 +43,6 @@ If you choose to accept the security warning, you will be presented with the log
* **User:** admin
* **Password:** passw0rd
If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable. If you supply a PKCS#12 keystore using the `MQ_TLS_KEYSTORE` environment variable, then the web console will be configured to use the certificate inside the keystore for HTTPS operations.
If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable.
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_ENABLE_EMBEDDED_WEB_SERVER` to `false`.

File diff suppressed because it is too large Load Diff

102
internal/tls/tls_web.go Normal file
View File

@@ -0,0 +1,102 @@
/*
© Copyright IBM Corporation 2019
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 tls
import (
"fmt"
"os"
"path/filepath"
"github.com/ibm-messaging/mq-container/internal/command"
"github.com/ibm-messaging/mq-container/internal/keystore"
)
// webServerKeystoreName is the name of the web server Keystore
const webServerKeystoreName = "default.p12"
// ConfigureWebTLS configures TLS for the web server
func ConfigureWebTLS(keyLabel string) error {
// Return immediately if we have no certificate to use as identity
if keyLabel == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") == "" {
return nil
}
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
tls := "tls.xml"
tlsConfig := filepath.Join(webConfigDir, tls)
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
err := os.Remove(tlsConfig)
if err != nil {
return fmt.Errorf("Failed to delete file %s: %v", tlsConfig, err)
}
// Symlink here to prevent issues on restart
err = os.Symlink(newTLSConfig, tlsConfig)
if err != nil {
return fmt.Errorf("Failed to create symlink %s->%s: %v", newTLSConfig, tlsConfig, err)
}
mqmUID, mqmGID, err := command.LookupMQM()
if err != nil {
return fmt.Errorf("Failed to find mqm user or group: %v", err)
}
err = os.Chown(tlsConfig, mqmUID, mqmGID)
if err != nil {
return fmt.Errorf("Failed to change ownership of %s to mqm: %v", tlsConfig, err)
}
return nil
}
// ConfigureWebKeyStore configures the Web Keystore
func ConfigureWebKeystore(p12Truststore KeyStoreData) (string, error) {
webKeystore := filepath.Join(keystoreDir, webServerKeystoreName)
// 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(webKeystore, p12Truststore.Password)
err := newWebKeystore.Create()
if err != nil {
return "", fmt.Errorf("Failed to create Web Keystore %s: %v", webKeystore, err)
}
// Generate a new self-signed certificate in the Web Keystore
err = newWebKeystore.CreateSelfSignedCertificate("default", fmt.Sprintf("CN=%s", genHostName), genHostName)
if err != nil {
return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystore, genHostName, err)
}
} else {
// Check Web Keystore already exists
_, err := os.Stat(webKeystore)
if err != nil {
return "", fmt.Errorf("Failed to find existing Web Keystore %s: %v", webKeystore, err)
}
}
// Check Web Truststore already exists
_, err := os.Stat(p12Truststore.Keystore.Filename)
if err != nil {
return "", fmt.Errorf("Failed to find existing Web Truststore %s: %v", p12Truststore.Keystore.Filename, err)
}
return webServerKeystoreName, nil
}

View File

@@ -81,8 +81,6 @@ func TestDevSecure(t *testing.T) {
"LICENSE=accept",
"MQ_QMGR_NAME=" + qm,
"MQ_APP_PASSWORD=" + appPassword,
"MQ_TLS_KEYSTORE=/var/tls/server.p12",
"MQ_TLS_PASSPHRASE=" + tlsPassPhrase,
"DEBUG=1",
},
Image: imageName(),
@@ -90,7 +88,7 @@ func TestDevSecure(t *testing.T) {
hostConfig := container.HostConfig{
Binds: []string{
coverageBind(t),
tlsDir(t, false) + ":/var/tls",
tlsDir(t, false) + ":/etc/mqm/pki/keys/default",
},
// Assign a random port for the web server on the host
// TODO: Don't do this for all tests

View File

@@ -82,7 +82,7 @@ func tlsDir(t *testing.T, unixPath bool) string {
// 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) {
containerConfig := container.Config{
// -e MQ_PORT_1414_TCP_ADDR=9.145.14.173 -e MQ_USERNAME=app -e MQ_PASSWORD=passw0rd -e MQ_CHANNEL=DEV.APP.SVRCONN -e MQ_TLS_KEYSTORE=/tls/test.p12 -e MQ_TLS_PASSPHRASE=passw0rd -v /Users/arthurbarr/go/src/github.com/ibm-messaging/mq-container/test/tls:/tls msgtest
// -e MQ_PORT_1414_TCP_ADDR=9.145.14.173 -e MQ_USERNAME=app -e MQ_PASSWORD=passw0rd -e MQ_CHANNEL=DEV.APP.SVRCONN -e MQ_TLS_TRUSTSTORE=/tls/test.p12 -e MQ_TLS_PASSPHRASE=passw0rd -v /Users/arthurbarr/go/src/github.com/ibm-messaging/mq-container/test/tls:/tls msgtest
Env: []string{
"MQ_PORT_1414_TCP_ADDR=" + getIPAddress(t, cli, ID),
"MQ_USERNAME=" + user,