Refactor TLS code
This commit is contained in:
2
Makefile
2
Makefile
@@ -162,7 +162,7 @@ build-devjmstest:
|
|||||||
test-devserver: test/docker/vendor
|
test-devserver: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
||||||
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
|
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
|
||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/ibm-messaging/mq-container/internal/metrics"
|
"github.com/ibm-messaging/mq-container/internal/metrics"
|
||||||
"github.com/ibm-messaging/mq-container/internal/name"
|
"github.com/ibm-messaging/mq-container/internal/name"
|
||||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func doMain() error {
|
func doMain() error {
|
||||||
@@ -147,19 +148,19 @@ func doMain() error {
|
|||||||
// Print out versioning information
|
// Print out versioning information
|
||||||
logVersionInfo()
|
logVersionInfo()
|
||||||
|
|
||||||
err = ConfigureTLSKeystores()
|
keylabel, cmsDB, p12Trust, _, err := tls.ConfigureTLSKeystores(keyDir, trustDir, keystoreDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ConfigureTLS(*devFlag)
|
err = configureTLS(keylabel, cmsDB, *devFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = postInit(name)
|
err = postInit(name, keylabel, p12Trust)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logTermination(err)
|
logTermination(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,38 +17,23 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
// postInit is run after /var/mqm is set up
|
// 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")
|
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
||||||
if enableWebServer == "true" || enableWebServer == "1" {
|
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)
|
// Configure the web server (if enabled)
|
||||||
err := configureWebServer()
|
keystore, err := configureWebServer(keylabel, p12Trust)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Start the web server, in the background (if installed)
|
// Start the web server, in the background (if installed)
|
||||||
// WARNING: No error handling or health checking available for the web server
|
// WARNING: No error handling or health checking available for the web server
|
||||||
go func() {
|
go func() {
|
||||||
startWebServer()
|
startWebServer(keystore, p12Trust.Password)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -16,448 +16,37 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
pwr "math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"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/command"
|
||||||
"github.com/ibm-messaging/mq-container/internal/keystore"
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
"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/"
|
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
|
// 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
|
// 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
|
// configureWebTLS configures TLS for Web Console
|
||||||
var p12TrustCerts []*pem.Block
|
func configureWebTLS(label string) error {
|
||||||
var cmsTrustCerts []*pem.Block
|
// Return immediately if we have no certificate to use as identity
|
||||||
var p12TrustKnownFingerPrints []string
|
if label == "" {
|
||||||
var cmsTrustKnownFingerPrints []string
|
return nil
|
||||||
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 {
|
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||||
p12TrustCerts = append(p12TrustCerts, block)
|
tls := "tls.xml"
|
||||||
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)
|
tlsConfig := filepath.Join(webConfigDir, tls)
|
||||||
newTLSConfig := filepath.Join(webConfigDir, tlsTemplate)
|
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
|
||||||
err := os.Remove(tlsConfig)
|
err := os.Remove(tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not delete file %s: %v", tlsConfig, err)
|
return fmt.Errorf("Could not delete file %s: %v", tlsConfig, err)
|
||||||
@@ -479,18 +68,8 @@ func ConfigureWebTLS() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureTLSDev configures TLS for developer defaults
|
// configureTLSDev configures TLS for developer defaults
|
||||||
func ConfigureTLSDev() error {
|
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 mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||||
const mqscTemplate string = mqsc + ".tpl"
|
const mqscTemplate string = mqsc + ".tpl"
|
||||||
const sslCipherSpec string = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
const sslCipherSpec string = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
||||||
@@ -516,30 +95,23 @@ func ConfigureTLSDev() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureTLS configures TLS for queue manager
|
// configureTLS configures TLS for queue manager
|
||||||
func ConfigureTLS(devmode bool) error {
|
func configureTLS(certLabel string, cmsKeystore tls.KeyStoreData, devmode bool) error {
|
||||||
if !keystoresConfigured {
|
|
||||||
err := ConfigureTLSKeystores()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debug("Configuring TLS")
|
log.Debug("Configuring TLS")
|
||||||
keyFile := filepath.Join(keystoreDir, CMSKeyStoreName)
|
|
||||||
|
|
||||||
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
||||||
const mqscTemplate string = mqsc + ".tpl"
|
const mqscTemplate string = mqsc + ".tpl"
|
||||||
|
|
||||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||||
"SSLKeyR": strings.TrimSuffix(keyFile, ".kdb"),
|
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
|
||||||
"CertificateLabel": qmKeyLabel,
|
"CertificateLabel": certLabel,
|
||||||
}, log)
|
}, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if devmode {
|
if devmode && certLabel != "" {
|
||||||
err = ConfigureTLSDev()
|
err = configureTLSDev()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -548,229 +120,30 @@ func ConfigureTLS(devmode bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureSSOTLS configures TLS for the Cloud Integration Platform Single Sign-On
|
// configureSSOTLS configures TLS for the Cloud Integration Platform Single Sign-On
|
||||||
func ConfigureSSOTLS() error {
|
func configureSSOTLS(p12TrustStore tls.KeyStoreData) (string, error) {
|
||||||
if !keystoresConfigured {
|
|
||||||
err := ConfigureTLSKeystores()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO find way to supply this
|
// TODO find way to supply this
|
||||||
// Override the webstore variables to hard coded defaults
|
// Override the webstore variables to hard coded defaults
|
||||||
webkeyStoreName = CIPDefaultLabel + ".p12"
|
webkeyStoreName := tls.IntegrationDefaultLabel + ".p12"
|
||||||
|
|
||||||
// Check keystore exists
|
// Check keystore exists
|
||||||
ks := filepath.Join(keystoreDir, webkeyStoreName)
|
ks := filepath.Join(keystoreDir, webkeyStoreName)
|
||||||
_, err := os.Stat(ks)
|
_, err := os.Stat(ks)
|
||||||
if err != nil {
|
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
|
// Check truststore exists
|
||||||
ts := filepath.Join(keystoreDir, P12TrustStoreName)
|
_, err = os.Stat(p12TrustStore.Keystore.Filename)
|
||||||
_, err = os.Stat(ts)
|
|
||||||
if err != nil {
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return webkeyStoreName, 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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
@@ -27,10 +26,12 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/command"
|
"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/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")
|
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(err) {
|
||||||
log.Debug("Skipping web server, because it's not installed")
|
log.Debug("Skipping web server, because it's not installed")
|
||||||
@@ -48,9 +49,9 @@ func startWebServer() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TLS enabled
|
// TLS enabled
|
||||||
if webkeyStoreName != "" {
|
if keystore != "" {
|
||||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webkeyStoreName)
|
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+keystore)
|
||||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keyStorePasswords)
|
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keystorepw)
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, gid, err := command.LookupMQM()
|
uid, gid, err := command.LookupMQM()
|
||||||
@@ -78,34 +79,8 @@ func startWebServer() error {
|
|||||||
log.Println("Started web server")
|
log.Println("Started web server")
|
||||||
return nil
|
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)
|
func configureSSO(p12TrustStore tls.KeyStoreData) (string, error) {
|
||||||
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 {
|
|
||||||
// Ensure all required environment variables are set for SSO
|
// Ensure all required environment variables are set for SSO
|
||||||
requiredEnvVars := []string{
|
requiredEnvVars := []string{
|
||||||
"MQ_WEB_ADMIN_USERS",
|
"MQ_WEB_ADMIN_USERS",
|
||||||
@@ -120,7 +95,7 @@ func configureSSO() error {
|
|||||||
}
|
}
|
||||||
for _, envVar := range requiredEnvVars {
|
for _, envVar := range requiredEnvVars {
|
||||||
if len(os.Getenv(envVar)) == 0 {
|
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)
|
_, err := os.Stat(mqwebDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process SSO template for generating file mqwebuser.xml
|
// Process SSO template for generating file mqwebuser.xml
|
||||||
adminUsers := strings.Split(os.Getenv("MQ_WEB_ADMIN_USERS"), "\n")
|
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)
|
err = mqtemplate.ProcessTemplateFile(mqwebDir+"/mqwebuser.xml.tpl", mqwebDir+"/mqwebuser.xml", map[string][]string{"AdminUser": adminUsers}, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure SSO TLS
|
// Configure SSO TLS
|
||||||
return ConfigureSSOTLS()
|
return configureSSOTLS(p12TrustStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureWebServer() error {
|
func configureWebServer(keyLabel string, p12Trust tls.KeyStoreData) (string, error) {
|
||||||
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
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 err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return keystore, nil
|
||||||
}
|
}
|
||||||
return err
|
return keystore, err
|
||||||
}
|
}
|
||||||
const webConfigDir string = "/etc/mqm/web"
|
const webConfigDir string = "/etc/mqm/web"
|
||||||
_, err = os.Stat(webConfigDir)
|
_, err = os.Stat(webConfigDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return keystore, nil
|
||||||
}
|
}
|
||||||
return err
|
return keystore, err
|
||||||
}
|
}
|
||||||
uid, gid, err := command.LookupMQM()
|
uid, gid, err := command.LookupMQM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return keystore, err
|
||||||
}
|
}
|
||||||
const prefix string = "/etc/mqm/web"
|
const prefix string = "/etc/mqm/web"
|
||||||
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
|
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
|
||||||
@@ -194,7 +187,7 @@ func configureWebServer() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := CopyFile(from, to)
|
err := copy.CopyFile(from, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -206,5 +199,5 @@ func configureWebServer() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return err
|
return keystore, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ 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_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_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_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** - 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_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** - Passphrase for the keystore referenced in `MQ_TLS_KEYSTORE`.
|
* **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
|
## Details of the default configuration
|
||||||
|
|
||||||
|
|||||||
@@ -96,3 +96,22 @@ docker exec \
|
|||||||
```
|
```
|
||||||
|
|
||||||
Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes.
|
Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes.
|
||||||
|
|
||||||
|
## Supplying TLS certificates
|
||||||
|
|
||||||
|
If you wish to supply TLS Certificates that the queue manager and MQ Console should use for TLS operations then you must supply the unencrypted PEM files for both the certificates and private keys in the following directories:
|
||||||
|
|
||||||
|
* `/etc/mqm/pki/keys/<Label>` - for certificates with public and private keys
|
||||||
|
* `/etc/mqm/pki/trust/<index>` - for certificates with only the public key
|
||||||
|
|
||||||
|
For example, if you have an identity certificate you wish to add with the label `mykey` and 2 certificates you wish to add as trusted then you would need to add the files into the following locations where files ending in `.key` contain private keys and `.crt` contain certificates:
|
||||||
|
|
||||||
|
- `/etc/mqm/pki/keys/mykey/tls.key`
|
||||||
|
- `/etc/mqm/pki/keys/mykey/tls.crt`
|
||||||
|
- `/etc/mqm/pki/keys/mykey/ca.crt`
|
||||||
|
- `/etc/mqm/pki/trust/0/tls.crt`
|
||||||
|
- `/etc/mqm/pki/trust/1/tls.crt`
|
||||||
|
|
||||||
|
This can be achieved by either mounting the directories or files into the container when you run it or by baking the files into the correct location in the image.
|
||||||
|
|
||||||
|
If you supply multiple identity certificates then the first label alphabetically will be chosen as the certificate to be used by the MQ Console and the default certificate for the queue manager. If you wish to use a different certificate on the queue manager then you can change the certificate to use at runtime by executing the MQSC command `ALTER QMGR CERTLABL('<newlabel>')`
|
||||||
|
|||||||
49
internal/copy/copy.go
Normal file
49
internal/copy/copy.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
© 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 copy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CopyFileMode(src, dest string, perm os.FileMode) error {
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ibm-messaging/mq-container/internal/command"
|
"github.com/ibm-messaging/mq-container/internal/command"
|
||||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyStore describes information about a keystore file
|
// KeyStore describes information about a keystore file
|
||||||
@@ -67,12 +66,11 @@ func NewPKCS12KeyStore(filename, password string) *KeyStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a key store, if it doesn't already exist
|
// Create a key store, if it doesn't already exist
|
||||||
func (ks *KeyStore) Create(log *logger.Logger) error {
|
func (ks *KeyStore) Create() error {
|
||||||
_, err := os.Stat(ks.Filename)
|
_, err := os.Stat(ks.Filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Keystore already exists so we should refresh it by deleting it.
|
// Keystore already exists so we should refresh it by deleting it.
|
||||||
extension := filepath.Ext(ks.Filename)
|
extension := filepath.Ext(ks.Filename)
|
||||||
log.Debugf("Refreshing keystore: %v", ks.Filename)
|
|
||||||
if ks.keyStoreType == "cms" {
|
if ks.keyStoreType == "cms" {
|
||||||
// Only delete these when we are refreshing the kdb keystore
|
// Only delete these when we are refreshing the kdb keystore
|
||||||
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
||||||
@@ -80,23 +78,19 @@ func (ks *KeyStore) Create(log *logger.Logger) error {
|
|||||||
crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
|
crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
|
||||||
err = os.Remove(stashFile)
|
err = os.Remove(stashFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error removing %s: %v", stashFile, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = os.Remove(rdbFile)
|
err = os.Remove(rdbFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error removing %s: %v", rdbFile, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = os.Remove(crlFile)
|
err = os.Remove(crlFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error removing %s: %v", crlFile, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = os.Remove(ks.Filename)
|
err = os.Remove(ks.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error removing %s: %v", ks.Filename, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if !os.IsNotExist(err) {
|
} else if !os.IsNotExist(err) {
|
||||||
@@ -112,22 +106,19 @@ func (ks *KeyStore) Create(log *logger.Logger) error {
|
|||||||
|
|
||||||
mqmUID, mqmGID, err := command.LookupMQM()
|
mqmUID, mqmGID, err := command.LookupMQM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = os.Chown(ks.Filename, mqmUID, mqmGID)
|
err = os.Chown(ks.Filename, mqmUID, mqmGID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateStash creates a key stash, if it doesn't already exist
|
// CreateStash creates a key stash, if it doesn't already exist
|
||||||
func (ks *KeyStore) CreateStash(log *logger.Logger) error {
|
func (ks *KeyStore) CreateStash() error {
|
||||||
extension := filepath.Ext(ks.Filename)
|
extension := filepath.Ext(ks.Filename)
|
||||||
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
||||||
log.Debugf("TLS stash file: %v", stashFile)
|
|
||||||
_, err := os.Stat(stashFile)
|
_, err := os.Stat(stashFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@@ -140,12 +131,10 @@ func (ks *KeyStore) CreateStash(log *logger.Logger) error {
|
|||||||
}
|
}
|
||||||
mqmUID, mqmGID, err := command.LookupMQM()
|
mqmUID, mqmGID, err := command.LookupMQM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = os.Chown(stashFile, mqmUID, mqmGID)
|
err = os.Chown(stashFile, mqmUID, mqmGID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
614
internal/tls/tls.go
Normal file
614
internal/tls/tls.go
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
/*
|
||||||
|
© 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 (
|
||||||
|
"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/copy"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/keystore"
|
||||||
|
pkcs "software.sslmate.com/src/go-pkcs12"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IntegrationDefaultLabel is the default certificate label used by Cloud Integration Platform
|
||||||
|
const IntegrationDefaultLabel = "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"
|
||||||
|
|
||||||
|
type KeyStoreData struct {
|
||||||
|
Keystore *keystore.KeyStore
|
||||||
|
Password string
|
||||||
|
TrustedCerts []*pem.Block
|
||||||
|
KnownFingerPrints []string
|
||||||
|
KeyLabels []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type P12KeyFiles struct {
|
||||||
|
Keystores []string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Keystores known certs (if not already there) and add to the
|
||||||
|
// Keystore if "addToKeystore" is true.
|
||||||
|
func addCertToKeyData(block *pem.Block, keyData *KeyStoreData, addToKeystore bool) error {
|
||||||
|
sha1str, err := getCertFingerPrint(block)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
known := false
|
||||||
|
for _, fingerprint := range keyData.KnownFingerPrints {
|
||||||
|
if fingerprint == sha1str {
|
||||||
|
known = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !known {
|
||||||
|
// Sometimes we don't want to add to the keystore trust here.
|
||||||
|
// For example if it will be imported with the key later.
|
||||||
|
if addToKeystore {
|
||||||
|
keyData.TrustedCerts = append(keyData.TrustedCerts, block)
|
||||||
|
}
|
||||||
|
keyData.KnownFingerPrints = append(keyData.KnownFingerPrints, sha1str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a random 12 character password from the characters a-z, A-Z, 0-9.
|
||||||
|
func generateRandomPassword() string {
|
||||||
|
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))])
|
||||||
|
}
|
||||||
|
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the PKCS#12 Truststore and the CMS Keystore.
|
||||||
|
func generateAllStores(dir string) (KeyStoreData, KeyStoreData, error) {
|
||||||
|
var cmsKeystore, p12TrustStore KeyStoreData
|
||||||
|
pw := generateRandomPassword()
|
||||||
|
|
||||||
|
cmsKeystore.Password = pw
|
||||||
|
p12TrustStore.Password = pw
|
||||||
|
|
||||||
|
// Create the keystore Directory (if it doesn't already exist)
|
||||||
|
os.MkdirAll(dir, 0775)
|
||||||
|
|
||||||
|
p12TrustStore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(dir, P12TrustStoreName), p12TrustStore.Password)
|
||||||
|
err := p12TrustStore.Keystore.Create()
|
||||||
|
if err != nil {
|
||||||
|
return cmsKeystore, p12TrustStore, fmt.Errorf("Failed to create PKCS#12 TrustStore: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(dir, CMSKeyStoreName), cmsKeystore.Password)
|
||||||
|
err = cmsKeystore.Keystore.Create()
|
||||||
|
if err != nil {
|
||||||
|
return cmsKeystore, p12TrustStore, fmt.Errorf("Failed to create CMS KeyStore: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmsKeystore, p12TrustStore, 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(keyDir, outputDir string, cmsKeyDB, p12TrustDB *KeyStoreData) (string, P12KeyFiles, error) {
|
||||||
|
var p12s P12KeyFiles
|
||||||
|
var firstLabel string
|
||||||
|
|
||||||
|
pwToUse := cmsKeyDB.Password
|
||||||
|
p12s.Password = pwToUse
|
||||||
|
trustStoreReserveredName := P12TrustStoreName[0 : len(P12TrustStoreName)-len(filepath.Ext(P12TrustStoreName))]
|
||||||
|
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()
|
||||||
|
if keyLabel == trustStoreReserveredName {
|
||||||
|
return firstLabel, p12s, fmt.Errorf("Found key with same label set as same name as truststore(%s). This is not allowed", trustStoreReserveredName)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 firstLabel, p12s, fmt.Errorf("Could not read keyfile %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||||
|
}
|
||||||
|
block, _ := pem.Decode(keyFile)
|
||||||
|
if block == nil {
|
||||||
|
return firstLabel, p12s, 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 firstLabel, p12s, err
|
||||||
|
}
|
||||||
|
//It was PKCS8 afterall
|
||||||
|
}
|
||||||
|
keyfilename = a.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keyfile == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 firstLabel, p12s, fmt.Errorf("Could not read file %s: %v", filepath.Join(keyDir, key.Name(), a.Name()), err)
|
||||||
|
}
|
||||||
|
block, _ := pem.Decode(cert)
|
||||||
|
if block == nil {
|
||||||
|
return firstLabel, p12s, 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 firstLabel, p12s, 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
|
||||||
|
err = addCertToKeyData(block, cmsKeyDB, false)
|
||||||
|
|
||||||
|
} else if strings.HasSuffix(a.Name(), ".crt") {
|
||||||
|
remainder, err := ioutil.ReadFile(filepath.Join(keyDir, key.Name(), a.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, 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
|
||||||
|
err = addCertToKeyData(block, cmsKeyDB, false)
|
||||||
|
|
||||||
|
// Add to the p12 truststore
|
||||||
|
err = addCertToKeyData(block, p12TrustDB, true)
|
||||||
|
|
||||||
|
caCert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, 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, pwToUse)
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, fmt.Errorf("Could not encode PKCS#12 Keystore %s: %v", keyLabel+".p12", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(outputDir, keyLabel+".p12"), file, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, fmt.Errorf("Could not write PKCS#12 Keystore %s: %v", filepath.Join(outputDir, keyLabel+".p12"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p12s.Keystores = append(p12s.Keystores, keyLabel+".p12")
|
||||||
|
|
||||||
|
// Add to the CMS keystore
|
||||||
|
err = cmsKeyDB.Keystore.Import(filepath.Join(outputDir, keyLabel+".p12"), pwToUse)
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, fmt.Errorf("Could not import keys from %s into CMS Keystore: %v", filepath.Join(outputDir, keyLabel+".p12"), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relabel it
|
||||||
|
allLabels, err := cmsKeyDB.Keystore.GetCertificateLabels()
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, fmt.Errorf("Could not list keys in CMS Keystore: %v", err)
|
||||||
|
}
|
||||||
|
relabelled := false
|
||||||
|
for _, cl := range allLabels {
|
||||||
|
found := false
|
||||||
|
for _, kl := range cmsKeyDB.KeyLabels {
|
||||||
|
if strings.Trim(cl, "\"") == kl {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
// This is the one to rename
|
||||||
|
err = cmsKeyDB.Keystore.RenameCertificate(strings.Trim(cl, "\""), keyLabel)
|
||||||
|
if err != nil {
|
||||||
|
return firstLabel, p12s, err
|
||||||
|
}
|
||||||
|
relabelled = true
|
||||||
|
cmsKeyDB.KeyLabels = append(cmsKeyDB.KeyLabels, keyLabel)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !relabelled {
|
||||||
|
return firstLabel, p12s, fmt.Errorf("Unable to find the added key for %s in CMS keystore", keyLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First key found so mark it as the one to use with the queue manager.
|
||||||
|
if firstLabel == "" {
|
||||||
|
firstLabel = keyLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstLabel, p12s, 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(trustDir string, cmsKeyDB, p12TrustDB *KeyStoreData) 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
|
||||||
|
err = addCertToKeyData(block, cmsKeyDB, true)
|
||||||
|
|
||||||
|
// Add to the p12 truststore
|
||||||
|
err = addCertToKeyData(block, p12TrustDB, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We've potentially created two lists of certificates to import. Add them both to relevant Truststores
|
||||||
|
if len(p12TrustDB.TrustedCerts) > 0 {
|
||||||
|
// Do P12 TrustStore first
|
||||||
|
temporaryPemFile := filepath.Join("/tmp", "trust.pem")
|
||||||
|
os.Remove(temporaryPemFile)
|
||||||
|
|
||||||
|
err := writeCertsToFile(temporaryPemFile, p12TrustDB.TrustedCerts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p12TrustDB.Keystore.AddNoLabel(temporaryPemFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not add certificates to PKCS#12 Truststore: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmsKeyDB.TrustedCerts) > 0 {
|
||||||
|
// Now the CMS Keystore
|
||||||
|
temporaryPemFile := filepath.Join("/tmp", "cmsTrust.pem")
|
||||||
|
os.Remove(temporaryPemFile)
|
||||||
|
|
||||||
|
err = writeCertsToFile(temporaryPemFile, cmsKeyDB.TrustedCerts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmsKeyDB.Keystore.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(keyDir, certDir, outputDir string) (string, KeyStoreData, KeyStoreData, P12KeyFiles, error) {
|
||||||
|
var returnLabel, label string
|
||||||
|
var cmsKeyDB, p12TrustDB KeyStoreData
|
||||||
|
var keyFiles P12KeyFiles
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cmsKeyDB, p12TrustDB, err = generateAllStores(outputDir)
|
||||||
|
if err != nil {
|
||||||
|
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handleIntegrationGeneratedCerts(keyDir)
|
||||||
|
if err != nil {
|
||||||
|
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
returnLabel, err = expandOldTLSVarible(keyDir, outputDir, &cmsKeyDB, &p12TrustDB)
|
||||||
|
if err != nil {
|
||||||
|
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
label, keyFiles, err = processKeys(keyDir, outputDir, &cmsKeyDB, &p12TrustDB)
|
||||||
|
if err != nil {
|
||||||
|
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||||
|
}
|
||||||
|
if returnLabel == "" {
|
||||||
|
returnLabel = label
|
||||||
|
}
|
||||||
|
|
||||||
|
err = processTrustCertificates(certDir, &cmsKeyDB, &p12TrustDB)
|
||||||
|
if err != nil {
|
||||||
|
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnLabel, cmsKeyDB, p12TrustDB, keyFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function supports an old mechanism of importing certificates
|
||||||
|
func handleIntegrationGeneratedCerts(keyDir string) error {
|
||||||
|
dir := "/mnt/tls"
|
||||||
|
outputdir := filepath.Join(keyDir, IntegrationDefaultLabel)
|
||||||
|
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", IntegrationDefaultLabel)
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("Failed to check that %s does not exist: %v", outputdir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll(outputdir, 0775)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not create %s: %v", outputdir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = copy.CopyFileMode(filepath.Join(dir, keyfile), filepath.Join(outputdir, keyfile), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not copy %s: %v", keyfile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = copy.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(keyDir, outputDir string, cmsKeyDB, p12TrustDB *KeyStoreData) (string, 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
|
||||||
|
overrideLabel := outputDirName
|
||||||
|
|
||||||
|
// Write out the certificate for the private key
|
||||||
|
if cert != nil {
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Headers: nil,
|
||||||
|
Bytes: cert.Raw,
|
||||||
|
}
|
||||||
|
err = addCertToKeyData(&block, cmsKeyDB, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("expandOldTLSVarible: Failed to add cert to CMS Keystore duplicate list: %v", err)
|
||||||
|
}
|
||||||
|
err = addCertToKeyData(&block, p12TrustDB, true)
|
||||||
|
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 = addCertToKeyData(&block, cmsKeyDB, 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 = addCertToKeyData(&block, p12TrustDB, true)
|
||||||
|
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(outputDir, outputDirName+".p12")
|
||||||
|
|
||||||
|
// Create p12 keystore
|
||||||
|
file, err := pkcs.Encode(rand.Reader, pk, cert, cas, p12TrustDB.Password)
|
||||||
|
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 = cmsKeyDB.Keystore.Import(destination, p12TrustDB.Password)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to import p12 keystore %s: %v", destination, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk != nil {
|
||||||
|
// Relabel the key
|
||||||
|
allLabels, err := cmsKeyDB.Keystore.GetCertificateLabels()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("cms GetCertificateLabels: %v\n", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
relabelled := false
|
||||||
|
for _, cl := range allLabels {
|
||||||
|
found := false
|
||||||
|
for _, kl := range cmsKeyDB.KeyLabels {
|
||||||
|
if strings.Trim(cl, "\"") == kl {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
// This is the one to rename
|
||||||
|
err = cmsKeyDB.Keystore.RenameCertificate(strings.Trim(cl, "\""), outputDirName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
relabelled = true
|
||||||
|
cmsKeyDB.KeyLabels = append(cmsKeyDB.KeyLabels, outputDirName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !relabelled {
|
||||||
|
return "", fmt.Errorf("Unable to find the added key in CMS keystore")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overrideLabel, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user