Configure Single-Sign-On for the web server
This commit is contained in:
committed by
Robert Parker
parent
9b3b1f7b9e
commit
4c1d124484
@@ -20,18 +20,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// postInit is run after /var/mqm is set up
|
// postInit is run after /var/mqm is set up
|
||||||
// This version of postInit is only included as part of the MQ Advanced for Developers build
|
|
||||||
func postInit(name string) error {
|
func postInit(name string) error {
|
||||||
disable := os.Getenv("MQ_DISABLE_WEB_CONSOLE")
|
disable := os.Getenv("MQ_DISABLE_WEB_CONSOLE")
|
||||||
if disable != "true" && disable != "1" {
|
if disable != "true" && disable != "1" {
|
||||||
|
|
||||||
|
// Configure Single-Sign-On for the web server (if enabled)
|
||||||
|
enableSSO := os.Getenv("MQ_ENABLE_SSO")
|
||||||
|
if enableSSO == "true" || enableSSO == "1" {
|
||||||
|
err := configureSSO()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the web server (if installed)
|
// Configure the web server (if installed)
|
||||||
err := configureWebServer()
|
err := configureWebServer()
|
||||||
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
|
||||||
// which is why it's limited to use with MQ Advanced for Developers only
|
|
||||||
go func() {
|
go func() {
|
||||||
startWebServer()
|
startWebServer()
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"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/keystore"
|
||||||
|
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startWebServer() error {
|
func startWebServer() error {
|
||||||
@@ -88,6 +91,101 @@ func CopyFile(src, dest string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureSSO() error {
|
||||||
|
|
||||||
|
// Ensure all required environment variables are set for SSO
|
||||||
|
requiredEnvVars := []string{
|
||||||
|
"MQ_WEB_ADMIN_USERS",
|
||||||
|
"MQ_OIDC_CLIENT_ID",
|
||||||
|
"MQ_OIDC_CLIENT_SECRET",
|
||||||
|
"MQ_OIDC_AUTHORIZATION_ENDPOINT",
|
||||||
|
"MQ_OIDC_TOKEN_ENDPOINT",
|
||||||
|
"MQ_OIDC_JWK_ENDPOINT",
|
||||||
|
"MQ_OIDC_ISSUER_IDENTIFIER",
|
||||||
|
"MQ_OIDC_CERTIFICATE",
|
||||||
|
}
|
||||||
|
for _, envVar := range requiredEnvVars {
|
||||||
|
if len(os.Getenv(envVar)) == 0 {
|
||||||
|
return fmt.Errorf("%v must be set when MQ_ENABLE_SSO=true", envVar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check mqweb directory exists
|
||||||
|
const mqwebDir string = "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||||
|
_, err := os.Stat(mqwebDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process SSO template for generating file mqwebuser.xml
|
||||||
|
adminUsers := strings.Split(os.Getenv("MQ_WEB_ADMIN_USERS"), ",")
|
||||||
|
err = mqtemplate.ProcessTemplateFile(mqwebDir+"/mqwebuser.xml.tpl", mqwebDir+"/mqwebuser.xml", map[string][]string{"AdminUser": adminUsers}, log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure SSO TLS
|
||||||
|
return configureSSO_TLS()
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureSSO_TLS() error {
|
||||||
|
|
||||||
|
// Create tls directory
|
||||||
|
dir := "/run/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("Creating self-signed certificate in key store")
|
||||||
|
err = ks.CreateSelfSignedCertificate("default", "CN=IBMMQWeb,O=IBM,OU=Platform,C=GB")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug("Importing self-signed certificate into trust store")
|
||||||
|
err = ts.Import(ks.Filename, ks.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug("Adding OIDC CA certificate to trust store")
|
||||||
|
err = ts.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func configureWebServer() error {
|
func configureWebServer() error {
|
||||||
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -150,6 +150,24 @@ func (ks *KeyStore) Import(inputFile, password string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
|
||||||
|
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn string) error {
|
||||||
|
out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a CA certificate to the keystore
|
||||||
|
func (ks *KeyStore) Add(inputFile, label string) error {
|
||||||
|
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetCertificateLabels returns the labels of all certificates in the key store
|
// GetCertificateLabels returns the labels of all certificates in the key store
|
||||||
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
||||||
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<server>
|
||||||
|
<featureManager>
|
||||||
|
<feature>openidConnectClient-1.0</feature>
|
||||||
|
<feature>ssl-1.0</feature>
|
||||||
|
</featureManager>
|
||||||
|
<enterpriseApplication id="com.ibm.mq.console">
|
||||||
|
<application-bnd>
|
||||||
|
<security-role name="MQWebAdmin">
|
||||||
|
<group name="MQWebUI" realm="defaultRealm"/>
|
||||||
|
{{- range $index, $element := .AdminUser}}
|
||||||
|
<user name="admin{{$index}}" access-id="{{.}}"/>
|
||||||
|
{{- end}}
|
||||||
|
</security-role>
|
||||||
|
</application-bnd>
|
||||||
|
</enterpriseApplication>
|
||||||
|
<enterpriseApplication id="com.ibm.mq.rest">
|
||||||
|
<application-bnd>
|
||||||
|
<security-role name="MQWebAdmin">
|
||||||
|
<group name="MQWebUI" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
<security-role name="MQWebUser">
|
||||||
|
<group name="MQWebMessaging" realm="defaultRealm"/>
|
||||||
|
</security-role>
|
||||||
|
</application-bnd>
|
||||||
|
</enterpriseApplication>
|
||||||
|
<openidConnectClient id="mqclient"
|
||||||
|
clientId="${env.MQ_OIDC_CLIENT_ID}"
|
||||||
|
clientSecret="${env.MQ_OIDC_CLIENT_SECRET}"
|
||||||
|
authorizationEndpointUrl="${env.MQ_OIDC_AUTHORIZATION_ENDPOINT}"
|
||||||
|
tokenEndpointUrl="${env.MQ_OIDC_TOKEN_ENDPOINT}"
|
||||||
|
scope="openid profile email"
|
||||||
|
inboundPropagation="supported"
|
||||||
|
jwkEndpointUrl="${env.MQ_OIDC_JWK_ENDPOINT}"
|
||||||
|
signatureAlgorithm="RS256"
|
||||||
|
issuerIdentifier="${env.MQ_OIDC_ISSUER_IDENTIFIER}">
|
||||||
|
</openidConnectClient>
|
||||||
|
<variable name="httpHost" value="*"/>
|
||||||
|
<keyStore id="MQWebKeyStore" location="/run/tls/key.jks" type="JKS" password="password"/>
|
||||||
|
<keyStore id="MQWebTrustStore" location="/run/tls/trust.jks" type="JKS" password="password"/>
|
||||||
|
<ssl id="thisSSLConfig" clientAuthenticationSupported="true" keyStoreRef="MQWebKeyStore" trustStoreRef="MQWebTrustStore" sslProtocol="TLSv1.2" serverKeyAlias="default"/>
|
||||||
|
<sslDefault sslRef="thisSSLConfig"/>
|
||||||
|
<httpDispatcher enableWelcomePage="false" appOrContextRootMissingMessage='<script>document.location.href="/ibmmq/console";</script>' />
|
||||||
|
</server>
|
||||||
Reference in New Issue
Block a user