Refactor TLS code

This commit is contained in:
Robert Parker
2019-05-29 16:37:20 +01:00
parent 8e22763f16
commit b64c060ef4
10 changed files with 764 additions and 741 deletions

View File

@@ -28,6 +28,7 @@ import (
"github.com/ibm-messaging/mq-container/internal/metrics"
"github.com/ibm-messaging/mq-container/internal/name"
"github.com/ibm-messaging/mq-container/internal/ready"
"github.com/ibm-messaging/mq-container/internal/tls"
)
func doMain() error {
@@ -147,19 +148,19 @@ func doMain() error {
// Print out versioning information
logVersionInfo()
err = ConfigureTLSKeystores()
keylabel, cmsDB, p12Trust, _, err := tls.ConfigureTLSKeystores(keyDir, trustDir, keystoreDir)
if err != nil {
logTermination(err)
return err
}
err = ConfigureTLS(*devFlag)
err = configureTLS(keylabel, cmsDB, *devFlag)
if err != nil {
logTermination(err)
return err
}
err = postInit(name)
err = postInit(name, keylabel, p12Trust)
if err != nil {
logTermination(err)
return err

View File

@@ -17,38 +17,23 @@ package main
import (
"os"
"github.com/ibm-messaging/mq-container/internal/tls"
)
// postInit is run after /var/mqm is set up
func postInit(name string) error {
func postInit(name, keylabel string, p12Trust tls.KeyStoreData) error {
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
if enableWebServer == "true" || enableWebServer == "1" {
// Configure TLS for Web Console first
if webkeyStoreName != "" {
err := ConfigureWebTLS()
if err != nil {
return err
}
}
// Configure Single-Sign-On for the web server (if enabled)
enableSSO := os.Getenv("MQ_BETA_ENABLE_SSO")
if enableSSO == "true" || enableSSO == "1" {
err := configureSSO()
if err != nil {
return err
}
}
// Configure the web server (if enabled)
err := configureWebServer()
keystore, err := configureWebServer(keylabel, p12Trust)
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() {
startWebServer()
startWebServer(keystore, p12Trust.Password)
}()
}
return nil

View File

@@ -16,448 +16,37 @@ limitations under the License.
package main
import (
"bufio"
"fmt"
"io/ioutil"
pwr "math/rand"
"os"
"path/filepath"
"strings"
"time"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"encoding/pem"
"github.com/ibm-messaging/mq-container/internal/command"
"github.com/ibm-messaging/mq-container/internal/keystore"
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
pkcs "software.sslmate.com/src/go-pkcs12"
"github.com/ibm-messaging/mq-container/internal/tls"
)
// Location to store the keystores
const keystoreDir = "/run/runmqserver/tls/"
// CIPDefaultLabel is the default certificate label used by Cloud Integration Platform
const CIPDefaultLabel = "default"
// P12TrustStoreName is the name of the PKCS#12 truststore used by the webconsole
const P12TrustStoreName = "trust.p12"
// CMSKeyStoreName is the name of the CMS Keystore used by the queue manager
const CMSKeyStoreName = "key.kdb"
// KeyStorePasswords The password of the keystores. Should never be printed!
var keyStorePasswords string
// KeyDir is the location of the certificate keys to import
const KeyDir = "/etc/mqm/pki/keys"
const keyDir = "/etc/mqm/pki/keys"
// TrustDir is the location of the Certifates to add
const TrustDir = "/etc/mqm/pki/trust"
const trustDir = "/etc/mqm/pki/trust"
// Used to track certificates and keys we've found to add
var p12TrustCerts []*pem.Block
var cmsTrustCerts []*pem.Block
var p12TrustKnownFingerPrints []string
var cmsTrustKnownFingerPrints []string
var cmsKeyLabels []string
// The keystore objects
var p12TrustStore *keystore.KeyStore
var cmsKeyStore *keystore.KeyStore
// Variables for storing the label of the certificate to use
var webkeyStoreName string
var qmKeyLabel string
// Tracks whether the keystores have been configured
var keystoresConfigured bool = false
func getCertFingerPrint(block *pem.Block) (string, error) {
// Add to future truststore and known certs (if not already there)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("could not parse x509 certificate: %v", err)
}
sha1Sum := sha1.Sum(cert.Raw)
sha1str := string(sha1Sum[:])
return sha1str, nil
}
// Add to future pkcs#12 Truststore and known certs (if not already there)
func addCertToP12TrustStoreNoDups(block *pem.Block) error {
sha1str, err := getCertFingerPrint(block)
if err != nil {
return err
}
known := false
for _, t := range p12TrustKnownFingerPrints {
if t == sha1str {
known = true
break
}
// configureWebTLS configures TLS for Web Console
func configureWebTLS(label string) error {
// Return immediately if we have no certificate to use as identity
if label == "" {
return nil
}
if !known {
p12TrustCerts = append(p12TrustCerts, block)
p12TrustKnownFingerPrints = append(p12TrustKnownFingerPrints, sha1str)
}
return nil
}
// Add to CMS Keystore known certs (if not already there) and add to the
// CMS Keystore if "addToKeystore" is true.
func addCertToCMSKeystoreNoDups(block *pem.Block, addToKeystore bool) error {
sha1str, err := getCertFingerPrint(block)
if err != nil {
return err
}
known := false
for _, t := range cmsTrustKnownFingerPrints {
if t == sha1str {
known = true
break
}
}
if !known {
// Sometimes we don't want to add to the CMS keystore trust here.
// For example if it will be imported with the key later.
if addToKeystore {
cmsTrustCerts = append(cmsTrustCerts, block)
}
cmsTrustKnownFingerPrints = append(cmsTrustKnownFingerPrints, sha1str)
}
return nil
}
// Generates a random 12 character password from the characters a-z, A-Z, 0-9.
func generateRandomPassword() {
pwr.Seed(time.Now().Unix())
validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
validcharArray := []byte(validChars)
password := ""
for i := 0; i < 12; i++ {
password = password + string(validcharArray[pwr.Intn(len(validcharArray))])
}
keyStorePasswords = password
}
// Creates the PKCS#12 Truststore and the CMS Keystore.
func generateAllStores() error {
generateRandomPassword()
// Create the keystore Directory (if it doesn't already exist)
os.MkdirAll(keystoreDir, 0775)
p12TrustStore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, P12TrustStoreName), keyStorePasswords)
err := p12TrustStore.Create(log)
if err != nil {
return fmt.Errorf("Failed to create PKCS#12 TrustStore: %v", err)
}
cmsKeyStore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, CMSKeyStoreName), keyStorePasswords)
err = cmsKeyStore.Create(log)
if err != nil {
return fmt.Errorf("Failed to create CMS KeyStore: %v", err)
}
return nil
}
// processKeys walks through the KeyDir directory and imports any keys it finds to individual PKCS#12 keystores
// and the CMS KeyStore. The label it uses is the name of the directory if finds the keys in.
func processKeys() error {
keyList, err := ioutil.ReadDir(KeyDir)
if err == nil && len(keyList) > 0 {
// Found some keys, verify the contents
for _, key := range keyList {
keys, _ := ioutil.ReadDir(filepath.Join(KeyDir, key.Name()))
keyLabel := key.Name()
keyfilename := ""
var keyfile interface{}
var certFile *x509.Certificate
var caFile []*x509.Certificate
// find the keyfile name
for _, a := range keys {
if strings.HasSuffix(a.Name(), ".key") {
keyFile, err := ioutil.ReadFile(filepath.Join(KeyDir, key.Name(), a.Name()))
if err != nil {
return fmt.Errorf("Could not read keyfile %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err)
}
block, _ := pem.Decode(keyFile)
if block == nil {
return fmt.Errorf("Could not decode keyfile %s: pem.Decode returned nil", filepath.Join(KeyDir, key.Name(), a.Name()))
}
//Test whether it is PKCS1
keyfile, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
// Before we fail check whether it is PKCS8
keyfile, err = x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
fmt.Printf("key %s ParsePKCS1/8PrivateKey ERR: %v\n", filepath.Join(KeyDir, key.Name(), a.Name()), err)
return err
}
//It was PKCS8 afterall
}
keyfilename = a.Name()
}
}
if keyfile == nil {
break
}
// Find out what the keyfile was called without the extension
prefix := keyfilename[0 : len(keyfilename)-len(filepath.Ext(keyfilename))]
for _, a := range keys {
if strings.HasSuffix(a.Name(), ".key") {
continue
}
if strings.HasPrefix(a.Name(), prefix) && strings.HasSuffix(a.Name(), ".crt") {
cert, err := ioutil.ReadFile(filepath.Join(KeyDir, key.Name(), a.Name()))
if err != nil {
return fmt.Errorf("Could not read file %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err)
}
block, _ := pem.Decode(cert)
if block == nil {
return fmt.Errorf("Could not decode certificate %s: pem.Decode returned nil", filepath.Join(KeyDir, key.Name(), a.Name()))
}
certFile, err = x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("Could not parse certificate %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err)
}
// Add to the dup list for the CMS keystore but not the PKCS#12 Truststore
addCertToCMSKeystoreNoDups(block, false)
} else if strings.HasSuffix(a.Name(), ".crt") {
remainder, err := ioutil.ReadFile(filepath.Join(KeyDir, key.Name(), a.Name()))
if err != nil {
return fmt.Errorf("Could not read file %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err)
}
for string(remainder) != "" {
var block *pem.Block
block, remainder = pem.Decode(remainder)
// If we can't decode the CA certificate then just exit.
if block == nil {
break
}
// Add to the dup list for the CMS keystore
addCertToCMSKeystoreNoDups(block, false)
// Add to the p12 truststore
addCertToP12TrustStoreNoDups(block)
caCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("Could not parse CA certificate %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err)
}
caFile = append(caFile, caCert)
}
}
}
// Create p12 keystore
file, err := pkcs.Encode(rand.Reader, keyfile, certFile, caFile, keyStorePasswords)
if err != nil {
return fmt.Errorf("Could not encode PKCS#12 Keystore %s: %v", keyLabel+".p12", err)
}
err = ioutil.WriteFile(filepath.Join(keystoreDir, keyLabel+".p12"), file, 0644)
if err != nil {
return fmt.Errorf("Could not write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keyLabel+".p12"), err)
}
// Add to the CMS keystore
err = cmsKeyStore.Import(filepath.Join(keystoreDir, keyLabel+".p12"), keyStorePasswords)
if err != nil {
return fmt.Errorf("Could not import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keyLabel+".p12"), err)
}
// Relabel it
allLabels, err := cmsKeyStore.GetCertificateLabels()
if err != nil {
return fmt.Errorf("Could not list keys in CMS Keystore: %v", err)
}
relabelled := false
for _, cl := range allLabels {
found := false
for _, kl := range cmsKeyLabels {
if strings.Trim(cl, "\"") == kl {
found = true
break
}
}
if !found {
// This is the one to rename
err = cmsKeyStore.RenameCertificate(strings.Trim(cl, "\""), keyLabel)
if err != nil {
return err
}
relabelled = true
cmsKeyLabels = append(cmsKeyLabels, keyLabel)
break
}
}
if !relabelled {
return fmt.Errorf("Unable to find the added key for %s in CMS keystore", keyLabel)
}
// First keystore so mark as the one to use with web console.
if webkeyStoreName == "" {
webkeyStoreName = keyLabel + ".p12"
}
// First key added to CMS Keystore so mark it as the one to use with the queue manager.
if qmKeyLabel == "" {
qmKeyLabel = keyLabel
}
}
}
return nil
}
// processTrustCertificates walks through the TrustDir directory and adds any certificates it finds
// to the PKCS#12 Truststore and the CMS KeyStore as long as has not already been added.
func processTrustCertificates() error {
certList, err := ioutil.ReadDir(TrustDir)
if err == nil && len(certList) > 0 {
// Found some keys, verify the contents
for _, cert := range certList {
certs, _ := ioutil.ReadDir(filepath.Join(TrustDir, cert.Name()))
for _, a := range certs {
if strings.HasSuffix(a.Name(), ".crt") {
remainder, err := ioutil.ReadFile(filepath.Join(TrustDir, cert.Name(), a.Name()))
if err != nil {
return fmt.Errorf("Could not read file %s: %v", filepath.Join(TrustDir, cert.Name(), a.Name()), err)
}
for string(remainder) != "" {
var block *pem.Block
block, remainder = pem.Decode(remainder)
if block == nil {
break
}
// Add to the CMS keystore
addCertToCMSKeystoreNoDups(block, true)
// Add to the p12 truststore
addCertToP12TrustStoreNoDups(block)
}
}
}
}
}
// We've potentially created two lists of certificates to import. Add them both to relevant Truststores
if len(p12TrustCerts) > 0 {
// Do P12 TrustStore first
temporaryPemFile := filepath.Join("/tmp", "trust.pem")
err := writeCertsToFile(temporaryPemFile, p12TrustCerts)
if err != nil {
return err
}
err = p12TrustStore.AddNoLabel(temporaryPemFile)
if err != nil {
return fmt.Errorf("Could not add certificates to PKCS#12 Truststore: %v", err)
}
}
if len(cmsTrustCerts) > 0 {
// Now the CMS Keystore
temporaryPemFile := filepath.Join("/tmp", "cmsTrust.pem")
err = writeCertsToFile(temporaryPemFile, cmsTrustCerts)
if err != nil {
return err
}
err = cmsKeyStore.AddNoLabel(temporaryPemFile)
if err != nil {
return fmt.Errorf("Could not add certificates to CMS keystore: %v", err)
}
}
return nil
}
// Writes a given list of certificates to a file.
func writeCertsToFile(file string, certs []*pem.Block) error {
f, err := os.Create(file)
if err != nil {
return fmt.Errorf("writeCertsToFile: Could not create file %s: %v", file, err)
}
defer f.Close()
w := bufio.NewWriter(f)
for i, c := range certs {
err := pem.Encode(w, c)
if err != nil {
return fmt.Errorf("writeCertsToFile: Could not encode certificate number %d: %v", i, err)
}
w.Flush()
}
return nil
}
// ConfigureTLSKeystores sets up the TLS Trust and Keystores for use
func ConfigureTLSKeystores() error {
err := generateAllStores()
if err != nil {
return err
}
err = handleCIPGeneratedCerts()
if err != nil {
return err
}
err = expandOldTLSVarible()
if err != nil {
return err
}
err = processKeys()
if err != nil {
return err
}
err = processTrustCertificates()
if err != nil {
return err
}
// set that the keystores have been configured
keystoresConfigured = true
return nil
}
// ConfigureWebTLS configures TLS for Web Console
func ConfigureWebTLS() error {
if !keystoresConfigured {
err := ConfigureTLSKeystores()
if err != nil {
return err
}
}
const webConfigDir string = "/etc/mqm/web/installations/Installation1/servers/mqweb"
const tls string = "tls.xml"
const tlsTemplate string = tls + ".tpl"
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
tls := "tls.xml"
tlsConfig := filepath.Join(webConfigDir, tls)
newTLSConfig := filepath.Join(webConfigDir, tlsTemplate)
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
err := os.Remove(tlsConfig)
if err != nil {
return fmt.Errorf("Could not delete file %s: %v", tlsConfig, err)
@@ -479,18 +68,8 @@ func ConfigureWebTLS() error {
return nil
}
// ConfigureTLSDev configures TLS for developer defaults
func ConfigureTLSDev() error {
if !keystoresConfigured {
err := ConfigureTLSKeystores()
if err != nil {
return err
}
}
if qmKeyLabel == "" {
// We haven't set a key to use so don't set the QM to use TLS
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"
const sslCipherSpec string = "TLS_RSA_WITH_AES_128_CBC_SHA256"
@@ -516,30 +95,23 @@ func ConfigureTLSDev() error {
return nil
}
// ConfigureTLS configures TLS for queue manager
func ConfigureTLS(devmode bool) error {
if !keystoresConfigured {
err := ConfigureTLSKeystores()
if err != nil {
return err
}
}
// configureTLS configures TLS for queue manager
func configureTLS(certLabel string, cmsKeystore tls.KeyStoreData, devmode bool) error {
log.Debug("Configuring TLS")
keyFile := filepath.Join(keystoreDir, CMSKeyStoreName)
const mqsc string = "/etc/mqm/15-tls.mqsc"
const mqscTemplate string = mqsc + ".tpl"
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
"SSLKeyR": strings.TrimSuffix(keyFile, ".kdb"),
"CertificateLabel": qmKeyLabel,
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
"CertificateLabel": certLabel,
}, log)
if err != nil {
return err
}
if devmode {
err = ConfigureTLSDev()
if devmode && certLabel != "" {
err = configureTLSDev()
if err != nil {
return err
}
@@ -548,229 +120,30 @@ func ConfigureTLS(devmode bool) error {
return nil
}
// ConfigureSSOTLS configures TLS for the Cloud Integration Platform Single Sign-On
func ConfigureSSOTLS() error {
if !keystoresConfigured {
err := ConfigureTLSKeystores()
if err != nil {
return err
}
}
// configureSSOTLS configures TLS for the Cloud Integration Platform Single Sign-On
func configureSSOTLS(p12TrustStore tls.KeyStoreData) (string, error) {
// TODO find way to supply this
// Override the webstore variables to hard coded defaults
webkeyStoreName = CIPDefaultLabel + ".p12"
webkeyStoreName := tls.IntegrationDefaultLabel + ".p12"
// Check keystore exists
ks := filepath.Join(keystoreDir, webkeyStoreName)
_, err := os.Stat(ks)
if err != nil {
return fmt.Errorf("Failed to find existing keystore %s: %v", ks, err)
return "", fmt.Errorf("Failed to find existing keystore %s: %v", ks, err)
}
// Check truststore exists
ts := filepath.Join(keystoreDir, P12TrustStoreName)
_, err = os.Stat(ts)
_, err = os.Stat(p12TrustStore.Keystore.Filename)
if err != nil {
return fmt.Errorf("Failed to find existing truststore %s: %v", ts, err)
return "", fmt.Errorf("Failed to find existing truststore %s: %v", p12TrustStore.Keystore.Filename, err)
}
// Add OIDC cert to the truststore
err = p12TrustStore.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC")
err = p12TrustStore.Keystore.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC")
if err != nil {
return err
return "", err
}
return nil
}
// This function supports the old mechanism of importing certificates supplied for
// Cloud Integration platform
func handleCIPGeneratedCerts() error {
dir := "/mnt/tls"
outputdir := filepath.Join(KeyDir, CIPDefaultLabel)
keyfile := "tls.key"
crtfile := "tls.crt"
// check that the files exist, if not just quietly leave as there's nothing to import
_, err := os.Stat(filepath.Join(dir, keyfile))
if err != nil {
return nil
}
_, err = os.Stat(filepath.Join(dir, crtfile))
if err != nil {
return nil
}
// Check the destination directory DOES not exist ahead of time
_, err = os.Stat(outputdir)
if err == nil {
return fmt.Errorf("Found CIP certificates to import but a TLS secret called %s is already present", CIPDefaultLabel)
} else if !os.IsNotExist(err) {
return fmt.Errorf("Failed to check that %s did not exist: %v", outputdir, err)
}
// We have certificate to import and no duplicates
err = os.MkdirAll(outputdir, 0775)
if err != nil {
return fmt.Errorf("Could not create %s: %v", outputdir, err)
}
err = CopyFileMode(filepath.Join(dir, keyfile), filepath.Join(outputdir, keyfile), 0644)
if err != nil {
return fmt.Errorf("Could not copy %s: %v", keyfile, err)
}
err = CopyFileMode(filepath.Join(dir, crtfile), filepath.Join(outputdir, crtfile), 0644)
if err != nil {
return fmt.Errorf("Could not copy %s: %v", keyfile, err)
}
// With certificates copied into place the rest of the TLS handling code will import them into the correct place
return nil
}
// This function supports the old mechanism of importing certificates supplied by the MQ_TLS_KEYSTORE envvar
func expandOldTLSVarible() error {
// TODO: Change this or find a way to set it
outputDirName := "acopiedcertificate"
// Check whether the old variable is set. If not exit quietly
keyfile := os.Getenv("MQ_TLS_KEYSTORE")
if keyfile == "" {
return nil
}
// There is a file to read and process
keyfilepw := os.Getenv("MQ_TLS_PASSPHRASE")
if !strings.HasSuffix(keyfile, ".p12") {
return fmt.Errorf("MQ_TLS_KEYSTORE (%s) does not point to a PKCS#12 file ending with the suffix .p12", keyfile)
}
_, err := os.Stat(keyfile)
if err != nil {
return fmt.Errorf("File %s referenced by MQ_TLS_KEYSTORE does not exist", keyfile)
}
readkey, err := ioutil.ReadFile(keyfile)
if err != nil {
return fmt.Errorf("Failed to read %s: %v", keyfile, err)
}
// File has been checked and read, decode it.
pk, cert, cas, err := pkcs.DecodeChain(readkey, keyfilepw)
if err != nil {
return fmt.Errorf("Failed to decode %s: %v", keyfile, err)
}
// Find a directory name that doesn't exist
for {
_, err := os.Stat(filepath.Join(KeyDir, outputDirName))
if err == nil {
outputDirName = outputDirName + "0"
} else {
break
}
}
//Bceause they supplied this certificate using the old method we should use this for qm & webconsole
webkeyStoreName = outputDirName + ".p12"
qmKeyLabel = outputDirName
// Write out the certificate for the private key
if cert != nil {
block := pem.Block{
Type: "CERTIFICATE",
Headers: nil,
Bytes: cert.Raw,
}
err = addCertToCMSKeystoreNoDups(&block, false)
if err != nil {
return fmt.Errorf("expandOldTLSVarible: Failed to add cert to CMS Keystore duplicate list: %v", err)
}
err = addCertToP12TrustStoreNoDups(&block)
if err != nil {
return fmt.Errorf("expandOldTLSVarible: Failed to add cert to P12 Truststore duplicate list: %v", err)
}
}
// now write out all the ca certificates
if cas != nil || len(cas) > 0 {
for i, c := range cas {
block := pem.Block{
Type: "CERTIFICATE",
Headers: nil,
Bytes: c.Raw,
}
// Add to the dup list for the CMS keystore
err = addCertToCMSKeystoreNoDups(&block, false)
if err != nil {
return fmt.Errorf("expandOldTLSVarible: Failed to add CA cert %d to CMS Keystore duplicate list: %v", i, err)
}
// Add to the p12 truststore
err = addCertToP12TrustStoreNoDups(&block)
if err != nil {
return fmt.Errorf("expandOldTLSVarible: Failed to add CA cert %d to P12 Truststore duplicate list: %v", i, err)
}
}
}
// Now we've handled the certificates copy the keystore into place
destination := filepath.Join(keystoreDir, outputDirName+".p12")
// Create p12 keystore
file, err := pkcs.Encode(rand.Reader, pk, cert, cas, keyStorePasswords)
if err != nil {
return fmt.Errorf("Failed to re-encode p12 keystore: %v", err)
}
err = ioutil.WriteFile(destination, file, 0644)
if err != nil {
return fmt.Errorf("Failed to write p12 keystore: %v", err)
}
// Add to the CMS keystore
err = cmsKeyStore.Import(destination, keyStorePasswords)
if err != nil {
return fmt.Errorf("Failed to import p12 keystore %s: %v", destination, err)
}
if pk != nil {
// Relabel the key
allLabels, err := cmsKeyStore.GetCertificateLabels()
if err != nil {
fmt.Printf("cms GetCertificateLabels: %v\n", err)
return err
}
relabelled := false
for _, cl := range allLabels {
found := false
for _, kl := range cmsKeyLabels {
if strings.Trim(cl, "\"") == kl {
found = true
break
}
}
if !found {
// This is the one to rename
err = cmsKeyStore.RenameCertificate(strings.Trim(cl, "\""), outputDirName)
if err != nil {
return err
}
relabelled = true
cmsKeyLabels = append(cmsKeyLabels, outputDirName)
break
}
}
if !relabelled {
return fmt.Errorf("Unable to find the added key in CMS keystore")
}
}
return nil
return webkeyStoreName, nil
}

View File

@@ -17,7 +17,6 @@ package main
import (
"fmt"
"io"
"os"
"os/exec"
"os/user"
@@ -27,10 +26,12 @@ import (
"syscall"
"github.com/ibm-messaging/mq-container/internal/command"
"github.com/ibm-messaging/mq-container/internal/copy"
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
"github.com/ibm-messaging/mq-container/internal/tls"
)
func startWebServer() error {
func startWebServer(keystore, keystorepw 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")
@@ -48,9 +49,9 @@ func startWebServer() error {
}
// TLS enabled
if webkeyStoreName != "" {
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webkeyStoreName)
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keyStorePasswords)
if keystore != "" {
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+keystore)
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keystorepw)
}
uid, gid, err := command.LookupMQM()
@@ -78,34 +79,8 @@ func startWebServer() error {
log.Println("Started web server")
return nil
}
func CopyFileMode(src, dest string, perm os.FileMode) error {
log.Debugf("Copying file %v to %v", src, dest)
in, err := os.Open(src)
if err != nil {
return fmt.Errorf("failed to open %s for copy: %v", src, err)
}
defer in.Close()
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm)
if err != nil {
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
err = out.Close()
return err
}
// CopyFile copies the specified file
func CopyFile(src, dest string) error {
return CopyFileMode(src, dest, 0770)
}
func configureSSO() error {
func configureSSO(p12TrustStore tls.KeyStoreData) (string, error) {
// Ensure all required environment variables are set for SSO
requiredEnvVars := []string{
"MQ_WEB_ADMIN_USERS",
@@ -120,7 +95,7 @@ func configureSSO() error {
}
for _, envVar := range requiredEnvVars {
if len(os.Getenv(envVar)) == 0 {
return fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
return "", fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
}
}
@@ -129,41 +104,59 @@ func configureSSO() error {
_, err := os.Stat(mqwebDir)
if err != nil {
if os.IsNotExist(err) {
return nil
return "", nil
}
return err
return "", err
}
// Process SSO template for generating file mqwebuser.xml
adminUsers := strings.Split(os.Getenv("MQ_WEB_ADMIN_USERS"), "\n")
err = mqtemplate.ProcessTemplateFile(mqwebDir+"/mqwebuser.xml.tpl", mqwebDir+"/mqwebuser.xml", map[string][]string{"AdminUser": adminUsers}, log)
if err != nil {
return err
return "", err
}
// Configure SSO TLS
return ConfigureSSOTLS()
return configureSSOTLS(p12TrustStore)
}
func configureWebServer() error {
_, err := os.Stat("/opt/mqm/bin/strmqweb")
func configureWebServer(keyLabel string, p12Trust tls.KeyStoreData) (string, error) {
var keystore string
// Configure TLS for Web Console first if we have a certificate to use
err := configureWebTLS(keyLabel)
if err != nil {
return keystore, err
}
if keyLabel != "" {
keystore = 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)
if err != nil {
return keystore, err
}
}
_, err = os.Stat("/opt/mqm/bin/strmqweb")
if err != nil {
if os.IsNotExist(err) {
return nil
return keystore, nil
}
return err
return keystore, err
}
const webConfigDir string = "/etc/mqm/web"
_, err = os.Stat(webConfigDir)
if err != nil {
if os.IsNotExist(err) {
return nil
return keystore, nil
}
return err
return keystore, err
}
uid, gid, err := command.LookupMQM()
if err != nil {
return err
return keystore, err
}
const prefix string = "/etc/mqm/web"
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
@@ -194,7 +187,7 @@ func configureWebServer() error {
return err
}
}
err := CopyFile(from, to)
err := copy.CopyFile(from, to)
if err != nil {
log.Error(err)
return err
@@ -206,5 +199,5 @@ func configureWebServer() error {
}
return nil
})
return err
return keystore, err
}