WebMethods Integration Server needs to connect and accept connectivity from a varied and large number of apps and systems, which requires authentication via different credentials, certificates and other sensitive data.
webMethods Integration Server has long provided a central tool called PassMan to ensure that all sensitive data is encrypted at source to avoid this data being divulged if exposed. PassMan has been updated in 11.1 to use the latest encryption algorithms for encryption, namely - PBKDF2/SHA256.
Any passwords, configuration or sensitive data can be encrypted by passman either implicitly, or programmatically via public services. All of the runtime's configuration can be externalized through configuration templates and they can also include passwords that can be encrypted in advance using the same password manager encryption key. Alternatively you can specify a kubernetes secrets via the syntax $secret{...}.
PassMan itself requires a secret called the master password to encrypt and decrypt the passwords written to the file system. This master password can be either injected when starting the container or persisted via the empw.dat file in the admin UI under security -> outbound passwords. However, this is not recommended in production usage as the master password file itself is only lightly encrypted!
Managing the life cycle of your secrets
Managing password is a necessary evil to keep platforms secure and made more complicated by the requirement that the changes are reflected across the integration landscape properly. For instance a common practice is password rotation where passwords are systematically updated on a regular basis, this requires both the target and source applications to be updated in parallel to avoid interruption. password can also be compromised, in which they need to be replaced immediately.
Different environments or instances may have different secrets for the same account, must be rotated regularly and shared safely and reliably with all interested parties.
Goal of a vault
A vault provides a secure central source of truth to define your secrets or sensitive data, which is then shared with the various apps in your landscape that require those secrets either as the target that must impose them, or for client applications to authenticate.
This also has the advantage that the administrator can manage the life cycle and versioning from a central location, whilst also avoid having this data distributed across multiple components, with all the inherent risk of them being exposed.
webMethods Passman vs Configuration
webMethods Integration Server allows you to use a vault to either replace the file system for persisting passwords via the built in passman (e.g. X), or inject passwords and other sensitive data as part of your deployment (e.g. Y).
The former is a good candidate for use in a traditional Integration Server running in a cluster where the administrator wants to be able to simply replace the file system with a vault, whilst allowing updates either through the local webMethods UI or remotely via the vault. However, this approach has a number of negatives; it is not practical for a container orientated deployment where the Integration Server will be headless, and it requires write access to the vault from the application layer in order for the passwords to be updated. Also passwords cannot be preloaded i.e. the initial configuration still needs to managed independently and up front via the app.
The alternative solution is as per Y, where we are no longer interested in passman at all and wish to simply inject the configuration into the runtime along with explicit references to the key/value pairs in the vault. This means that the application only requires read access to the vault and that the passwords must all be defined in advance and managed entirely via the vault. The negative is that we need to add the necessary configuration to map the webMethods configuration to the correct key/value pairs in the vault.
This article will focus on the latter use case as it is the most applicable in a modern data architecture and is generally a better solution overall.
webMethods Integration Server and Hashicorp Vault
webMethods Integration Server 11.1 (Core Fix6) can now be configured to connect to Hashicorp vault via a simple set of configuration variables. There is a no UI and the configuration is generic so that we can implement other vault providers in the future as demand dictates. We chose not to implement a UI as we wanted a solution that can be easily automated injected prior to starting the server/runtime.
Please ensure that you have updated your Integration Server or Microservices Runtime to 11.1 and Core Fix6 as a minimum!
Setting up Hashicorp Vault
Before we get started we need to set up a Hashicorp vault to use. We will do this via a container as it is the easiest setup, but you will still need to install some CLI tools locally on your computer. Also this article is not about Hashicorp vault and the setup here is rudimentary and aimed simply at getting a working solution up and running as quickly as possible. It is neither secure nor reliable and you should refer to the official documentation before attempting to setup an environment for test or production use.
Installing Hashicorp Vault
Spinning up the container
Make sure that you have installed a container engine such as Docker Desktop or Rancher Desktop and then run the following command
$ docker run --cap-add=IPC_LOCK --network my-net --name hashi-vault -e 'VAULT_LOCAL_CONFIG={"storage": {"file": {"path": "/vault/file"}}, "listener": [{"tcp": { "address": "0.0.0.0:8200", "tls_disable": true}}], "default_lease_ttl": "168h", "max_lease_ttl": "720h", "ui": true}' -p 8800:8800 -e 'VAULT_DEV_ROOT_TOKEN_ID=root' -e 'VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8800' hashicorp/vault
The above command will launch a very limited vault that uses a http connection for all communication, which is obviously completely wrong and no configuration will be persisted if the container is restarted.
You can check if it is up and running by accessing the following URL in a browser http://localhost:8800, the token will be 'root'
Installing the CLI tools
You can install the CLI tools directly on macOS if you have already installed homebrew or download the archive and install manually.
macOS
brew tap hashicorp/tap
brew install hashicorp/tap/vault
You will need to configure the CLI tools to connect to your vault, namely
$ export VAULT_ADDR='http://0.0.0.0:8800'
$ vault login root
You will then be able to use the CLI tool to manage your vault server e.g.
$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.18.3
Build Date 2024-12-16T14:00:53Z
Storage Type file
Cluster Name vault-cluster-f7a793e2
Cluster ID 57282cae-f724-4aba-640a-fea6751eff93
HA Enabled false
Configure some secrets
The below command will setup some secrets in our secrets engine, we will go into more detail about them later when we discuss the different use cases. For now just verify that it works.
$ vault kv put secret/my-is admin_password='monkeys' helloworld_greeting='Bonjour' mysql_username='scratch' mysql_password='manage'
If you login to your vault via the web browser http://localhost:8800 and navigate to your secret engine you should see the following
A secret called 'my-is' with 4 unique key/value pairs.
Setting up accessibility
The access given to an application so that it can access secrets in the vault has to be very tightly controlled for obvious reasons. There are multiple ways of doing that with HashiCorp, but we will focus on one called AppRole
As per Hashicorp's description an AppRole "An "AppRole" represents a set of Vault policies and login constraints that must be met to receive a token with those policies."
So we need to create a policy to define the level of access for our app, then assign it to AppRole, which will provide us with the necessary credentials to connect with.
Create the following file
testapp-policy.hcl
path "/secret/my-is/data/*" {
capabilities = ["read", "list"]
}
and then run the following command to load the into your vault
$ vault policy write testapp-policy testapp-policy.hcl
This policy will ensure that our app can only read or list secrets from the path my-is in the default secrets engine, To create the app run the following command
$ vault auth enable approle
$ vault write auth/approle/role/wm-is \
token_policies="testapp-policy" \
token_type=batch \
secret_id_ttl=10m \
token_ttl=20m \
token_max_ttl=30m \
secret_id_num_uses=40
The command references both our access policy and the rotation period for both our secret id and access tokens. You may want to adjust the time to live (ttl) for your secret id to avoid having to regenerate it to often.
Fetching the credentials to access the vault
Now that we have our AppRole we can fetch the role and secret id that will be needed to generate the access token.
$ vault read auth/approle/role/wm-is/role-id
$ vault write -f auth/approle/role/wm-is/secret-id
The role id will never change, but as per our configuration above, the secret id will expire after only 10 minutes and so the second command will have to be re-executed each time you want to get an access token.
Generating an access token
The following command can be used to generate an access token using the role and secret id you obtained above
$ curl --insecure --request POST --data '{"role_id":"<ROLE_ID>", "secret_id":"<SECRET_ID>"}' \
http://localhost:8800/v1/auth/approle/login
The result will contain access token you use to connect webMethods to the vault, but we will have to come back to this because we haven't yet configured our runtime.
Setting up the webMethods runtime
As stated above, we will use Configuration Variables to map key/value pairs from our vault into webMethods properties.
We will split our configuration into two parts, a configuration file to define the connection to the vault and a second file to map the secrets.
#### Configuration file for Connecting to Hashicorp Vault
Create the following file
vault.properties
vault.myis.type=com.webmethods.is.vault.hashicorp.HashiCorpVaultConnection
vault.myis.enabled=true
vault.myis.primary=false
vault.myis.secretsCache.enabled=true
vault.myis.secretsCache.maxEntries=1000
vault.myis.secretsCache.ttlSeconds=300
vault.myis.properties.SECRET_MOUNT_PATH=my-is
vault.myis.properties.ENCRYPTION_KEY=wm-key
vault.myis.properties.VAULT_ADDR=$env{VAULT_ADDR}
vault.myis.properties.VAULT_TOKEN=$env{VAULT_TOKEN}
This will configure a vault alias call 'myis' that will then expose the vault url and api key as the environment variables 'VAULT_ADDR' and 'VAULT_TOKEN'. We will come back to the environment variables in the section where we discuss starting up the container.
vault.<alias>.primary=true||false
This attribute indicates whether the vault operates to replace the file system used by passman (primary=true), our example here uses the secondary use cases where primary=false. We will instead map the configuration that requires the vault explicitly.
vault.<alias>.secretsCache
To avoid saturating the vault with requests and improve latency, you can configure a cache where you can configure a maximum capacity and also a time to live for each secret before it is automatically flushed. You can set ttl to -1 if you don't want secrets to be flushed, and in either case you can flush credentials by calling the services pub.vault:clearCache or pub.vault:refreshCache.
vault.<alias>.properties.SECRET_MOUNT
This property indicates the secret to be used to store all of the key/value pairs for the given vault. In our use case we have a secret called 'my-is' that will prefix all our secrets e.g. /v1/secret/data/my-is
vault kv get -field=admin_password secret/my-is
Those of you who are familiar with Hashicorp will be aware that a mount point normally refers to which secrets engine to use, we will adopt that in a future release
vault.<alias>.properties.ENCRPTION_KEY
This corresponds to the hashicorp transit alias, which determines what encryption algorithm to use when transporting the secret, you can set the key up with
$ vault secrets enable transit
$ vault write transit/keys/wm-key type=aes256-gcm96
Configuration file for mapping secrets
Create the following file
secrets.properties
user.Administrator.password=$vault{myis#admin_password}
globalvariable.JcHelloWorldAPI.greeting=$vault{myis#helloworld_greeting}
artConnection.JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.serverName=$env{MYSQL_HOST}
artConnection.JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.user=$vault{myis#mysql_username}
artConnection.JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.password=$vault{myis#mysql_password}
This file will map the webMethods properties to the known key/value pairs in our vault. The right hand side references the property webMethods to be updated and the left hand side provides the value via the vault modifier $vault{<alias>#<key>}.
The vault modifier consists of a vault alias and the key into the secret defined above by SECRET_MOUNT_PATH, and separated by the hash '#' symbol. The file then updates the following properties;
- The password for the Administrator user is mapped to the secret 'my-is/admin_password'
- Updates the package variable 'greeting' in the package JcHelloWorldAPI to 'my-is/helloworld_greeting'
- Updates the 'user' and 'password' credentials for a JDBC connection 'jc.helloworld._priv.jdbc:conn' in the 'JcHelloWorldAPI' package with the values from 'my-is/mysql_username' and 'my-is/mysql_password'
Refer to the official webMethods document on Configuration Variables for a complete list of the available variables.
Building our webMethods runtime image
For our purposes we will containerize our solution and so we need a Dockerfile to build our MSR image including all of the above configuration.
Dockerfile
FROM ibmwebmethods.azurecr.io/webmethods-microservicesruntime:11.1.0.5
COPY wpm.yml /opt/softwareag/wpm/wpm.yml
COPY vault.properties /opt/softwareag/IntegrationServer/vault.properties
COPY secrets.properties /opt/softwareag/IntegrationServer/secrets.properties
#COPY application.properties /opt/softwareag/IntegrationServer/application.properties
#COPY vault-cert.pem /opt/vault-cert.pem
ENV SAG_IS_CONFIG_PROPERTIES=vault.properties,secrets.properties
ENV VAULT_ADDR=http://hashi-vault:8800
ENV MYSQL_HOST=mysql
RUN /opt/softwareag/wpm/bin/wpm update -wr licensed -j <WPM_TOKEN> WmJDBCAdapter
RUN /opt/softwareag/wpm/bin/wpm.sh update -r HelloWorld JcHelloWorldAPI
The image includes a configuration file for using wpm (webMethods Package Manager), which is our tool for adding webMethods packages to your runtime including both IBM webMethods packages and your own. Refer to this guide on how to use it Our new delivery channel, webMethods Package Manager
You will need to include the following configuration file with your own git id and access token so that wpm can pull the demo package JcHelloWorldAPI. wpm does not allow anonymous downloads even though the package is in a public git repo.
wpm.yml
version: 1
switches:
# relative path of the target installation of IS (-d)
#target_installation: /Applications/SoftwareAG/11.0/IntegrationServer
target_installation: /opt/softwareag/IntegrationServer
# scan branches for versions (-sb)
scan_branches: true
# delete contents of SCM on a successful install, or update (-kr)
cleanup: true
# source repos ( -r repeating switch )
repositories:
# define your remote registries here (wpm or git) and reference them using -wr
default:
type: wpr
location: https://packages.webmethods.io
working_dir: /tmp
licensed:
type: wpr
registry: licensed
location: https://packages.webmethods.io
working_dir: /tmp
creds:
token: xxx
HelloWorld:
type: git
location: https://github.com/johnpcarter
working_dir: /tmp
creds:
user: <YOUR GITHUB ID>
password: <YOUR PASSWORD>
The Dockerfile then pulls in the configuration templates files for configuring the vault and secrets. It includes environment variables so that your Integration Server will load the files and allow the vault and database endpoints to be dynamically set when starting the container. Finally it includes both the official JDBC adapter and a demo package called JcHelloWorldAPI to showcase the different types of secrets you can replace.
To build the image simply run the following command inside the directory/folder where you have created the above files e.g.
$ ls
Dockerfile secrets.properties testapp-policy.hcl wpm.yml vault.properties
$ docker build . -t webmethods-edge-runtime-vault:11.2.0
Starting the MSR container
Before you can spin up your container you will need to generate an API key, which will require the AppRole id and it's secret_id as described above. Then replace the '<VAULT_TOKEN>' string in the command below with the returned API key.
$ docker run -p 4444:5555 -it --network my-net -e MYSQL_HOST=mysql -e VAULT_TOKEN=<VAULT_TOKEN> webmethods-edge-runtime-vault:11.2.0
You should see something like the following
HOST_DIR=
SERVICE_NAME=
Running in a container
/health endpoint access set to Anonymous
/metrics endpoint access set to Anonymous
Starting Software AG Microservices Runtime
2025-09-30 07:16:56 GMT [ISS.0028.0127I] Successfully parsed properties to add vault connection with alias: 'myis'
2025-09-30 07:16:56 GMT [ISS.0028.0130I] Successfully updated file: '/opt/softwareag/IntegrationServer/bin/../config/vaults.json'
Sep 30, 2025 7:16:56 AM com.webmethods.sc.security.passman.impl.masterpw.FileMasterPassword loadObjects
WARNING: loadObjects(config/empw.dat) threw this: type(com.webmethods.sc.security.passman.masterpw.MasterPasswordException), msg(Retrieved null/empty master file: config/empw.dat)
Sep 30, 2025 7:16:56 AM com.webmethods.sc.security.passman.impl.masterpw.FileMasterPassword loadObjects
WARNING: loadObjects(config/empw.dat.bak) threw this: type(com.webmethods.sc.security.passman.masterpw.MasterPasswordException), msg(Retrieved null/empty master file: config/empw.dat.bak)
Sep 30, 2025 7:16:57 AM com.webmethods.sc.security.passman.impl.PassMan initMasterLastChangedDate
WARNING: Found invalid last-changed date, null, using default
Sep 30, 2025 7:16:57 AM com.webmethods.sc.security.passman.impl.PassMan initMasterExpireInterval
WARNING: Found invalid expire interval, null, using default
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Validating License file for Microservices
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Validating License file for Microservices
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Processor found for the assetType globalvariable
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Processing properties...
2025-09-30 07:16:57 GMT [ISS.0028.9999I] {JcHelloWorldAPI.greeting.value="bonjour"}
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Processor found for the assetType artConnection
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Processing properties...
2025-09-30 07:16:57 GMT [ISS.0028.9999I] {JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.user=$vault{myis#mysql_username}, JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.password=******, JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.serverName=$env{MYSQL_HOST}}
2025-09-30 07:16:57 GMT [ISS.0028.0017W] Encryptor is creating password handle because the given password handle wm.is.art.password.jc.helloworld._priv.jdbc:conn does not exist in the password store.
2025-09-30 07:16:57 GMT [ISS.0028.0122I] Configuration Variables are encrypted using IS_DEFAULT_ENCRYPTION_KEY.
2025-09-30 07:16:57 GMT [ISS.0028.0008I] Set password in secure password store. Password handle: wm.is.art.password.jc.helloworld._priv.jdbc:conn
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Processor found for the assetType user
2025-09-30 07:16:57 GMT [ISS.0028.9999I] Processing properties...
2025-09-30 07:16:57 GMT [ISS.0028.9999I] {Administrator.password=******}
2025-09-30 07:16:59 GMT [ISS.0028.0024I] Changed password for user Administrator.
With the key lines being
2025-09-25 09:19:51 GMT [ISS.0028.0127I] Successfully parsed properties to add vault connection with alias: 'myis'
2025-09-25 09:19:51 GMT [ISS.0028.0130I] Successfully updated file: '/opt/softwareag/IntegrationServer/bin/../config/vaults.json'
which shows that the vault configuration is valid and has been loaded. The global variable substitution can be seen here
2025-09-25 09:19:52 GMT [ISS.0028.9999I] Processor found for the assetType globalvariable
2025-09-25 09:19:52 GMT [ISS.0028.9999I] Processing properties...
2025-09-25 09:19:52 GMT [ISS.0028.9999I] {JcHelloWorldAPI.greeting.value=$vault{myis#helloworld_greeting}}
The connection properties are updated here
2025-09-25 09:28:16 GMT [ISS.0028.9999I] Processor found for the assetType artConnection
2025-09-25 09:28:16 GMT [ISS.0028.9999I] Processing properties...
2025-09-25 09:28:16 GMT [ISS.0028.9999I] {JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.user=$vault{myis#mysql_username}, JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.password=******, JcHelloWorldAPI.jc.helloworld._priv.jdbc.conn.connectionSettings.host=$env{MYSQL_HOST}}
2025-09-25 09:28:16 GMT [ISS.0028.0017W] Encryptor is creating password handle because the given password handle wm.is.art.password.jc.helloworld._priv.jdbc:conn does not exist in the password store.
as well as the Administrator password
025-09-25 09:19:52 GMT [ISS.0028.9999I] Processor found for the assetType user
2025-09-25 09:19:52 GMT [ISS.0028.9999I] Processing properties...
2025-09-25 09:19:52 GMT [ISS.0028.9999I] {Administrator.password=******}
2025-09-25 09:19:54 GMT [ISS.0028.0024I] Changed password for user Administrator.
After spinning up your container, you can verify all is in order by accessing the local admin UI at http://localhost:4444
Don't forget that we overrode the Administrator password with the password 'monkeys', which will be your first indicator that everything has gone correctly. After succesfully logging in you should verify that both the global variable is now 'bonjour' and that the connection is enabled e.g.
Next steps
You now have the minimal setup for testing and experimenting with using a vault to secure all of your secrets required by your webMethods Integrations. However, you now need to ensure that;
- Setup a production quality secure and reliable production vault; Our use case has a single node and no persistence. In the real world the vault is a classic single point of failure (SPOF), if it is unavailable then nothing works. It is important that your vault is hardened
- Use TLS port for secure transport; The tutorial used a http connection which is inherently insecure, normal setup would have used a https port 8200. In which case you would also include the vault public certificate in your setup to ensure that webMethods can verify that it is in fact communicating with your vault and not an imposter. This certificate would have to be configured in both the vault and included in your webMethods Dockerfile and vault settings e.g.
vault.myis.properties.TLS_CERT_FILE=$file{./vault-cert.pem}
Explore possible authentication types; In our example we used AppRole authentication that requires the client to specify a role id and secret, in order to fetch an API key. This implicates that you can keep the secret and resulting API key secure. However Hashicorp provides a wide range of authentication types that may better suit your local environment and concerns.
If you want to stick with AppRole, then you will need to ensure that the API key is refreshed each time you spin up the container. Here is an example shell script to show this
start-wm.sh
#!/bin/bash
# === CONFIGURATION ===
VAULT_ADDR="http://localhost:8800"
ROLE_ID="<YOUR APPROLE ID>" # Replace with your AppRole role_id
SECRET_ID=$1 # assumes you will provide it when calling the command
# === Authenticate and Get Token ===
echo "🔐 Logging in to Vault using AppRole..."
RESPONSE=$(curl -s --request POST \
--data "{\"role_id\":\"$ROLE_ID\", \"secret_id\":\"$SECRET_ID\"}" \
"$VAULT_ADDR/v1/auth/approle/login")
# === Extract token ===
VAULT_TOKEN=$(echo "$RESPONSE" | jq -r '.auth.client_token')
if [[ "$VAULT_TOKEN" == "null" || -z "$VAULT_TOKEN" ]]; then
echo "❌ Failed to get VAULT_TOKEN. Response:"
echo "$RESPONSE"
exit 1
fi
echo "✅ VAULT_TOKEN acquired."
# export VAULT_TOKEN
# === Run docker command ===
docker run -p 7777:5555 -e VAULT_TOKEN=${VAULT_TOKEN} webmethods-edge-runtime-vault:11.2.0
For Kubernetes then use the Hashicorp vault  method to authenticate using the k8s service generated account token.