This guide describes how confidential information can be handled in a secured manner in the ESB environment. From the context of the ESB confidential information refers mainly to passwords. But confidential information could be anything and hence instructions given in this guide applies to all such confidential information elements.
The word 'secret' is used to refer to anything which needs to be kept as a secret. Eventhough the current usage is limited to passwords, API supports any type of data. Secret retrieving is done through 'SecretCallbackHandler' implementations. Currently there are a few implementations and more implementations can be added as needed.
This is the default implementation used within the JBoss Application Server to secure database passwords. It uses a hardcoded pass phrase (Need to use same pass phrase that have been used for encryption). Admin can encrypt passwords using a pass phrase and hard code that in the 'JBossEncryptionSecretCallbackHandler' and make the source code closed.
This is a command line based approach. Jline provides a way of reading passwords without echoing text - similar to jdk 6 scanners.
This is only supported in Synapse. With this we must use a JMX Console to provide secrets. This may be useful for any users that manage servers in a production environment using JMX.
Admin needs to hard code password and make source code closed. This can be used to just provide keystore passwords and other secrets can be retrieved using 'SecretManagerSecretCallbackHandler' (described next)
All secrets are managed using the Secret Manager. Secret Manager can keep any number of secret repositories. Those are arranged in a cascading manner. All secrets are stored in one place - secret repositories. Secrets can be accessed by providing aliases for them (no need to put encrypted values inline in application configurations). Key stores required by the Secret Manager and secret repositories are configurable. It can be done through the 'secret-conf.properties'. Currently, there is only one secret repository and it is called the FileBaseSecretRepository. It uses cipher-text.properties to keep secrets. It keeps aliases vs. its actual secret in encrypted format (encrypted by a key in keystore)
Example
secret-conf.properties
############################################################################ # Secret Securing Configuration ############################################################################ # The following property specifies a global password provider implementation # which will be used globally if not overriden in specific configurations #carbon.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler> #Examples: #carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.SecretManagerSecretCallbackHandler #carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.JMXSecretCallbackHandler #carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.JlineSecretCallbackHandler #carbon.secretProvider=org.apache.synapse.commons.security.secret.handler.JBossEncryptionSecretCallbackHandler #Secret Repositories configuration secretRepositories=file secretRepositories.file.provider=org.apache.synapse.commons.security.secret.repository.filebased.FileBaseSecretRepositoryProvider secretRepositories.file.location=cipher-text.properties #KeyStores configurations keystore.identity.location=resources/security/wso2carbon.jks keystore.identity.type=JKS keystore.identity.alias=wso2carbon keystore.identity.store.password=wso2carbon #keystore.identity.store.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler> keystore.identity.key.password=wso2carbon #keystore.identity.key.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler> #keystore.identity.parameters=enableHostnameVerifier=false;keyStoreCertificateFilePath=/home/esb.cer keystore.trust.location=resources/security/client-truststore.jks keystore.trust.type=JKS keystore.trust.alias=wso2carbon keystore.trust.store.password=wso2carbon #keystore.trust.store.secretProvider=<any implementation of org.apache.synapse.commons.security.secret.SecretCallbackHandler>
cipher-text.properties
aliases=esb # configuration per each plaintext esb.secret=M6U74dMVvRm4XFMczki2qZ6CsTvnUuRTjSditlACR5vTISSMI7F/mCTVJGOGdKJjij+VWVhBtmAOkElyvR9TwlUECnZ1o5DNsTK6l8je+9amc/ziTQLP3Q1tzm/Ex1pzHsG6jPGGrv3O0B9pZTfYFqRvlcNhM7Ve3WvA3ibs4Yk= esb.secret.alias=wso2carbon esb.secret.keystore=identity
For security operations (encrypt / decrypt), Secret Manager uses a KeyStore and securing the keystore passwords can be done by any of above mentioned ways. In the current implementation, there is only a one Secret Repository - A file based Secret Repository. It is possible to implements secret repositories based on database, directory servers, registry etc.
Consider that an application has a configuration for passwords as
<datasource> <password>pass</password> <secretProvider>any implementation of SecretCallbackHandler- class name</secretProvider> </datasource>
For JBossEncryptionSecretCallbackHandler, 'password' should contain an encrypted value. In any other case, it is just an alias- can keep any human readable text. For example in Secret Manger, actual password is in secret repositories. For file based repository, it is something like bellow.
aliases=pass pass.secret=EsY65tztE9R5b9pErVxLp8Br5d3ol6vRdWAkYHdc7XkZteGf37VJ+iNlCenqxYSEto0vcjpcmmzwf7K2wd9u3KQtVGKEoNLSe2LYZtrm3tKmGd6PX9YpdN72ml3JISNXPJ69yybFi6DVUIJfE5MFOd7gswWfCnkmZ3eJ6M1nuiI=
To retrieve secret, it is needed to use following API and follow steps.
Read the configuration parameter value of 'secret Provider' - say 'secretProvider'
// Create SecretCallbackHandler SecretCallbackHandler secretCallbackHanlder SecretCallbackHandlerFactory.createSecretCallbackHandler(secretProvider); // Setup SecretLoadingModule with CallbackHandlersSecretLoadingModule secretLoadingModule = new SecretLoadingModule(); secretLoadingModule.init(new SecretCallbackHandler[]{secretCallbackHanlder}); // Create SecretCallbacks SingleSecretCallback secretCallback = new SingleSecretCallback(aliases); // for above example 'aliases' is 'pass' secretLoadingModule.load(new SecretCallback[]{secretCallback}); String actualPassword= secretCallback.getSecret();
If you want to load multiple secrets at once you can use 'MultiSecretCallback'; or array of SingleSecretCallback s.
There is a simple way to avoid code duplication.
SecretInformation secretInformation = SecretInformationFactory.createSecretInformation(secretProvider, aliasPassword, passwordPrompt); String actualPassword = secretInformation.getResolvedSecret();
All 'SecretCallbackHandlers' other than the 'SecretManagerSecretCallbackHandler' are stateless enabling to be used without any additional code. With 'SecretManagerSecretCallbackHandler', it is best if there is only one 'SecretManager' instance. More precisely, a single 'SecretManagerSecretCallbackHandler' instance. For this, there is a carbon module called 'secretvault'. It just reads 'secret-conf.properties' and initiates a new 'SecretCallbackHandler' instance. This can be a 'SecretManagerSecretCallbackHandler' or any other 'SecretCallbackHandlers' instance that needs to be shared globally. To acccess this instance one must follow the instuctions given below.
Step 1 - Adding Dependencies
<dependency> <groupId>org.apache.synapse</groupId> <artifactId>synapse-commons</artifactId> <version>1.10.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.wso2.carbon</groupId> <artifactId>org.wso2.carbon.securevault</artifactId> <version>${carbon.version}</version> </dependency>
Setp 2 - Listening to SecretCallbackHandlerService
/** @scr.reference name="secret.callback.handler.service" * interface="org.wso2.carbon.securevault.SecretCallbackHandlerService" * cardinality="1..1" policy="dynamic" * bind="setSecretCallbackHandlerService" unbind="unsetSecretCallbackHandlerService */ protected void setSecretCallbackHandlerService( SecretCallbackHandlerService secretCallbackHandlerService) { Logic on Set } protected void unsetSecretCallbackHandlerService(SecretCallbackHandlerService secretCallbackHandlerService) { Logic on Unset }
The annotations at the top are required and the Maven SCR plugin must be used to build the code. Please refer Maven 2 documentation on how to use the Maven SCR plugin. SCR plugin generates OSGi declarative service descriptors by parsing the scr annotations stated in the source files.
Logic on Set
If the secret that needs to be resolved can be accessed directlym, for example passing as an argument, you can use the following logic.
SecretCallbackHandler secretProvider = secretCallbackHandlerService.getSecretCallbackHandler()); SecretInformation secretInformation = SecretInformationFactory.createSecretInformation(secretProvider, aliasPassword, passwordPrompt); String actualPassword = secretInformation.getResolvedSecret();
Now you can pass either an actual password or a SecretProvider or SecretInformation to the code location that uses the password. But if the code that performs secret resolution can not be accessed, for example, when using a configuration such as follows - you cannot pass resolved password directly.
<datasource> <password>pass</password> <secretProvider>any implementation of SecretCallbackHandler- class name</secretProvider> </datasource>
You have to do following if the scenario is similar to the above.
if (secretCallbackHandlerService != null) { SecretCallbackHandler secretCallbackHandler = secretCallbackHandlerService.getSecretCallbackHandler(); SharedSecretCallbackHandlerCache.getInstance().setSecretCallbackHandler(secretCallbackHandler); }
Then you have to specify secretProvider in the configuration as org.apache.synapse.commons.security.secret.handler.SharedSecretCallbackHandler. This is another SecretCallbackHandler that uses the handler set in the SharedSecretCallbackHandlerCache. You need to use Retrieving API. What we do here is using OSGI services in order to share across any component and use the Singleton pattern to share across classes in a single component and enable using the Retrieving API as it is.
This is a simple tool to encrypt and decrypt simple texts.There are scripts named ciphertool.sh & ciphertool.bat available in the bin directory to launch the tool. The arguments accepted by this tool with their meanings are shown bellow.
An example
ciphertool.bat -source testpass -keystore resources/security/client-truststore.jks -storepass wso2carbon -alias wo2carbon -outencode base64 -trusted