Refactor TLS code
This commit is contained in:
committed by
Stephen D Marshall
parent
ce184408df
commit
956b4a8e49
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
102
internal/tls/tls_web.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user