Add TLS Support
This commit is contained in:
@@ -153,14 +153,6 @@ func doMain() error {
|
||||
logTerminationf("Error getting queue manager name: %v", err)
|
||||
return err
|
||||
}
|
||||
ks, set := os.LookupEnv("MQ_TLS_KEYSTORE")
|
||||
if set {
|
||||
err = configureTLS(name, ks, os.Getenv("MQ_TLS_PASSPHRASE"))
|
||||
if err != nil {
|
||||
logTerminationf("Error configuring TLS: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = configureWeb(name)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,166 +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"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/keystore"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
)
|
||||
|
||||
func configureWebTLS(cms *keystore.KeyStore) error {
|
||||
dir := "/run/runmqdevserver/tls"
|
||||
ks := keystore.NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password)
|
||||
ts := keystore.NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password)
|
||||
|
||||
log.Debug("Creating key store")
|
||||
err := ks.Create(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Creating trust store")
|
||||
err = ts.Create(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Importing keys")
|
||||
err = ks.Import(cms.Filename, cms.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
tlsConfig := filepath.Join(webConfigDir, "tls.xml")
|
||||
newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml")
|
||||
err = os.Remove(tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we symlink here to prevent issues on restart
|
||||
err = os.Symlink(newTLSConfig, tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(tlsConfig, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureTLS(qmName string, inputFile string, passPhrase string) error {
|
||||
log.Debug("Configuring TLS")
|
||||
|
||||
_, err := os.Stat(inputFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Use a persisted file (on the volume) instead?
|
||||
dir := "/run/runmqdevserver/tls"
|
||||
keyFile := filepath.Join(dir, "key.kdb")
|
||||
|
||||
_, err = os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// #nosec G301
|
||||
err = os.MkdirAll(dir, 0770)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(dir, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cms := keystore.NewCMSKeyStore(keyFile, passPhrase)
|
||||
|
||||
err = cms.Create(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cms.CreateStash(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cms.Import(inputFile, passPhrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
labels, err := cms.GetCertificateLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(labels) == 0 {
|
||||
return fmt.Errorf("unable to find certificate label")
|
||||
}
|
||||
log.Debugf("Renaming certificate from %v", labels[0])
|
||||
const newLabel string = "devcert"
|
||||
err = cms.RenameCertificate(labels[0], newLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sslCipherSpec string
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
||||
} else {
|
||||
sslCipherSpec = "' '"
|
||||
}
|
||||
|
||||
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
|
||||
err = mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLKeyR": filepath.Join(dir, "key"),
|
||||
"CertificateLabel": newLabel,
|
||||
"SSLCipherSpec": sslCipherSpec,
|
||||
}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = configureWebTLS(cms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -147,6 +147,18 @@ func doMain() error {
|
||||
// Print out versioning information
|
||||
logVersionInfo()
|
||||
|
||||
err = ConfigureTLSKeystores()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = ConfigureTLS(*devFlag)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = postInit(name)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
|
||||
@@ -23,6 +23,13 @@ import (
|
||||
func postInit(name string) 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")
|
||||
|
||||
776
cmd/runmqserver/tls.go
Normal file
776
cmd/runmqserver/tls.go
Normal file
@@ -0,0 +1,776 @@
|
||||
/*
|
||||
© 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 (
|
||||
"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"
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
// TrustDir is the location of the Certifates to add
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
tlsConfig := filepath.Join(webConfigDir, tls)
|
||||
newTLSConfig := filepath.Join(webConfigDir, tlsTemplate)
|
||||
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 {
|
||||
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
|
||||
}
|
||||
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
const sslCipherSpec string = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
||||
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLCipherSpec": sslCipherSpec,
|
||||
}, 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(devmode bool) error {
|
||||
if !keystoresConfigured {
|
||||
err := ConfigureTLSKeystores()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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,
|
||||
}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if devmode {
|
||||
err = ConfigureTLSDev()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find way to supply this
|
||||
// Override the webstore variables to hard coded defaults
|
||||
webkeyStoreName = CIPDefaultLabel + ".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)
|
||||
}
|
||||
|
||||
// Check truststore exists
|
||||
ts := filepath.Join(keystoreDir, P12TrustStoreName)
|
||||
_, err = os.Stat(ts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to find existing truststore %s: %v", ts, err)
|
||||
}
|
||||
|
||||
// Add OIDC cert to the truststore
|
||||
err = p12TrustStore.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC")
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/keystore"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
)
|
||||
|
||||
@@ -44,7 +43,16 @@ func startWebServer() error {
|
||||
if !set {
|
||||
// Take all current environment variables, and add the app password
|
||||
cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd")
|
||||
} else {
|
||||
cmd.Env = os.Environ()
|
||||
}
|
||||
|
||||
// TLS enabled
|
||||
if webkeyStoreName != "" {
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webkeyStoreName)
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keyStorePasswords)
|
||||
}
|
||||
|
||||
uid, gid, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -70,17 +78,18 @@ func startWebServer() error {
|
||||
log.Println("Started web server")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyFile copies the specified file
|
||||
func CopyFile(src, dest string) error {
|
||||
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 err
|
||||
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, 0770)
|
||||
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)
|
||||
@@ -91,8 +100,12 @@ func CopyFile(src, dest string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func configureSSO() error {
|
||||
// CopyFile copies the specified file
|
||||
func CopyFile(src, dest string) error {
|
||||
return CopyFileMode(src, dest, 0770)
|
||||
}
|
||||
|
||||
func configureSSO() error {
|
||||
// Ensure all required environment variables are set for SSO
|
||||
requiredEnvVars := []string{
|
||||
"MQ_WEB_ADMIN_USERS",
|
||||
@@ -129,63 +142,7 @@ func configureSSO() error {
|
||||
}
|
||||
|
||||
// Configure SSO TLS
|
||||
return configureSSO_TLS()
|
||||
}
|
||||
|
||||
func configureSSO_TLS() error {
|
||||
|
||||
// Create tls directory
|
||||
dir := "/run/tls"
|
||||
mntdir := "/mnt/tls/"
|
||||
_, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0770)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(dir, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Setup key store & trust store
|
||||
ks := keystore.NewJKSKeyStore(filepath.Join(dir, "key.jks"), "password")
|
||||
ts := keystore.NewJKSKeyStore(filepath.Join(dir, "trust.jks"), "password")
|
||||
|
||||
log.Debug("Creating key store")
|
||||
err = ks.Create(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Creating trust store")
|
||||
err = ts.Create(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Generating PKCS12 file")
|
||||
err = ks.GeneratePKCS12(filepath.Join(mntdir, "tls.key"), filepath.Join(mntdir, "tls.crt"), filepath.Join(dir, "tls.p12"), "default", "password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Importing certificate into key store")
|
||||
err = ks.Import(filepath.Join(dir, "tls.p12"), "password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Adding OIDC certificate to trust store")
|
||||
err = ts.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC")
|
||||
return err
|
||||
return ConfigureSSOTLS()
|
||||
}
|
||||
|
||||
func configureWebServer() error {
|
||||
|
||||
Reference in New Issue
Block a user