2015/01/19
19 Jan, 2015

How to Run WSO2 API Manager 1.8.0 with Java Security Manager

  • Sanjeewa Malalgoda
  • Director - Engineering | Architect at WSO2 - WSO2

Java security manager is a component that defines and validates security policies for applications. With this, you can define certain policies for unsafe or sensitive operations. Any actions that are not allowed by the security policy will result in a SecurityException to be thrown. This article will discuss details of the Java security manager and explain how you can use it with WSO2 API Manager.

Introduction

WSO2 API Manager is a complete solution for publishing APIs, creating and managing a developer community, and for scalably routing API traffic. It leverages proven, production-ready, integration, security and governance components from WSO2 Enterprise Service Bus, WSO2 Identity Server, and WSO2 Governance Registry. Moreover, it is powered by WSO2 Business Activity Monitor, thereby making WSO2 API Manager ready for any large-scale deployments right away.

Java security manager is a component that defines and validates security policies for applications. With this, you can define certain policies for unsafe or sensitive operations. Any actions that are not allowed by the security policy will result in a SecurityException to be thrown. This article will discuss details of the Java security manager and explain how you can use it with WSO2 API Manager.

Applies to

WSO2 API Manager Version 1.8.0 and above

Table of contents

  1. Java security manager
  2. How Java security manager works
  3. Advantages of running a product with security manager
  4. How to enable security manager in WSO2 products
  5. Sample policy file
  6. Test this use case
  7. Conclusion

 

 

Java security manager

Java security manager is a component that defines and validate security policies for applications. This policy specifies actions that are unsafe or sensitive. Any actions that are not allowed by the security policy cause a SecurityException to be thrown. An application can also query its security manager to discover which actions are allowed. Since WSO2 API Manager is running on JVM, we can use this component to limit some user actions.

 

How Java security manager works

The Java API enforces the custom security policy by asking the security manager for permission to take any action before it does something that is potentially unsafe. For each potentially unsafe action, there is a method in the security manager that defines whether or not that action is allowed by the sandbox. Each method's name starts with "check," so, for example, checkRead() defines whether or not a thread is allowed to read to a specified file, and checkWrite() defines whether or not a thread is allowed to write to a specified file. The implementation of these methods is what defines the custom security policy of WSO2 API Manager. If we haven't defined any policies on sec.policy file, a default permission set will be applied to the program. By default, all permissions are disabled in the system.

Some of the activities that are regulated by a "check" method are listed below. The classes of the Java API check with the security manager before they perform the following operations.

Socket connection from a specified host and port number.

Modify a thread or thread group.

Socket connection to a specified host and port number.

Create a new class loader.

Delete a specified file.

Create a new process.

Cause the application to exit.

Wait for a connection on a specified local port number

Load a class from a specified package (used by class loaders)

Add a new class to a specified package (used by class loaders)

Access or modify system properties

Access a specified system property

Read from a specified file

Write (copy, move etc.) to a specified file

Advantages of running a product with security manager

If we consider WSO2 products, we normally allow external developers to deploy different types of artifacts within the product. When it comes to production deployments, however, we need to allow external developers to deploy applications inside product. But, at the same time, we need to apply some enforcements for their applications. As an example, users can deploy web applications in the WSO2 application server, but they cannot write to the local file system in the server or they shouldn't delete/copy files in the server. If we allow external users to write data to the local file system then they can easily fill the server with some garbage data. We can prevent such actions by enabling java security manager in all WSO2 products.

How to enable security manager in WSO2 products

Let's see how we can run WSO2 API Manager 1.8.0 with security manager enabled.

First we need to sign jar files in the products. To sign the jars, we need a key. We can use the keytool command to generate a key.

sanjeewa@sanjeewa-ThinkPad-T530:~/work/wso2am-1.8.0-1$ keytool -genkey -alias signFiles -keyalg RSA -keystore signkeystore.jks -validity 3650 -dname "CN=Sanjeewa,OU=Engineering, O=WSO2, L=Colombo, ST=Western, C=LK"Enter keystore password:

Re-enter new password:

Enter key password for

(RETURN if same as keystore password):

Then let's create scripts to sign Jars available in product. Create the 2 scripts as follows and grant them the required permissions (to execute them in the terminal).

signJars.sh script:

    #!/bin/bash
    if [[ ! -d $1 ]]; then
       echo "Please specify a target directory"
       exit 1
    fi
    for jarfile in `find . -type f -iname \*.jar`
    do
      ./signJar.sh $jarfile
    done

signJar.sh script:

    #!/bin/bash
    set -e
    jarfile=$1
    keystore_file="signkeystore.jks"
    keystore_keyalias='signFiles'
    keystore_storepass='wso2123'
    keystore_keypass='wso2123'
    signjar="$JAVA_HOME/bin/jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore $keystore_file -storepass $keystore_storepass -keypass $keystore_keypass"
    verifyjar="$JAVA_HOME/bin/jarsigner -keystore $keystore_file -verify"
    echo "Signing $jarfile"
    $signjar $jarfile $keystore_keyalias
    echo "Verifying $jarfile"
    $verifyjar $jarfile
    # Check whether the verification is successful.
    if [ $? -eq 1 ]
    then
       echo "Verification failed for $jarfile"
    fi

Then sign all jars using the above created scripts

  
  ./signJars.sh ./repository/ > log

Add following to wso2server.sh file

 -Djava.security.manager=org.wso2.carbon.bootstrap.CarbonSecurityManager \
 -Djava.security.policy=$CARBON_HOME/repository/conf/sec.policy \
 -Drestricted.packages=sun.,com.sun.xml.internal.ws.,com.sun.xml.internal.bind.,com.sun.imageio.,org.wso2.carbon. \  
 -Ddenied.system.properties=javax.net.ssl.trustStore,javax.net.ssl.trustStorePassword,denied.system.properties \

Exporting public key certificate and importing it to wso2carbon.jks

We need to import the public key certificate to the wso2carbon.jks as the security policy file will be referring to the signer certificate from the wso2carbon.jks (as specified by the first line).

    $ keytool -export -keystore signkeystore.jks -alias signFiles -file sign-cert.cer 
    sanjeewa@sanjeewa-ThinkPad-T530:~/work/wso2am-1.8.0-1$ keytool -import -alias signFiles -file sign-cert.cer -keystore repository/resources/security/wso2carbon.jks
    Enter keystore password:  
    Owner: CN=Sanjeewa, OU=Engineering, O=WSO2, L=Colombo, ST=Western, C=LK
    Issuer: CN=Sanjeewa, OU=Engineering, O=WSO2, L=Colombo, ST=Western, C=LK
    Serial number: 5486f3b0
    Valid from: Tue Dec 09 18:35:52 IST 2014 until: Fri Dec 06 18:35:52 IST 2024
    Certificate fingerprints:
    MD5:  54:13:FD:06:6F:C9:A6:BC:EE:DF:73:A9:88:CC:02:EC
    SHA1: AE:37:2A:9E:66:86:12:68:28:88:12:A0:85:50:B1:D1:21:BD:49:52
    Signature algorithm name: SHA1withRSA
    Version: 3
    Trust this certificate? [no]:  yes
    Certificate was added to keystore

Sample policy file

  keystore "file:${user.dir}/repository/resources/security/wso2carbon.jks", "JKS";

    // ========= Carbon Server Permissions ===================================
    grant {
       // Allow socket connections for any host
       permission java.net.SocketPermission "*:1-65535", "connect,resolve";
       // Allow to read all properties. Use -Ddenied.system.properties in wso2server.sh to restrict properties
       permission java.util.PropertyPermission "*", "read";
       permission java.lang.RuntimePermission "getClassLoader";
       // CarbonContext APIs require this permission
       permission java.lang.management.ManagementPermission "control";
       // Required by any component reading XMLs. For example: org.wso2.carbon.databridge.agent.thrift:4.2.1.
       permission java.lang.RuntimePermission "accessClassInPackage.com.sun.xml.internal.bind.v2.runtime.reflect";
       // Required by org.wso2.carbon.ndatasource.core:4.2.0. This is only necessary after adding above permission.
       permission java.lang.RuntimePermission "accessClassInPackage.com.sun.xml.internal.bind";
     permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/publisher/localhost/publisher/site/conf/locales/jaggery/locale_en.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/publisher/localhost/publisher/site/conf/locales/jaggery/locale_default.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/publisher/site/conf/site.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/store/localhost/store/site/conf/locales/jaggery/locale_en.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/store/localhost/store/site/conf/locales/jaggery/locale_default.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/store/site/conf/locales/jaggery/locale_en.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/store/site/conf/locales/jaggery/locale_default.json", "read,write";
      permission java.io.FilePermission "${carbon.home}/repository/deployment/server/jaggeryapps/store/site/conf/site.json", "read,write";
       permission javax.management.MBeanServerPermission "findMBeanServer,createMBeanServer";
      permission javax.management.MBeanPermission "-#-[-]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.MemoryImpl#*[java.lang:type=Memory]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.MemoryImpl#*[java.lang:type=Memory]", "getMBeanInfo";
      permission javax.management.MBeanPermission "sun.management.MemoryImpl#*[java.lang:type=Memory]", "getAttribute";
      permission javax.management.MBeanPermission "sun.management.MemoryPoolImpl#*[java.lang:type=MemoryPool,name=*]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.MemoryPoolImpl#*[java.lang:type=MemoryPool,name=*]", "getMBeanInfo";
      permission javax.management.MBeanPermission "sun.management.MemoryPoolImpl#*[java.lang:type=MemoryPool,name=*]", "getAttribute";
      permission javax.management.MBeanPermission "sun.management.GarbageCollectorImpl#*[java.lang:type=GarbageCollector,name=*]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.GarbageCollectorImpl#*[java.lang:type=GarbageCollector,name=*]", "getMBeanInfo";
      permission javax.management.MBeanPermission "sun.management.GarbageCollectorImpl#*[java.lang:type=GarbageCollector,name=*]", "getAttribute";
      permission javax.management.MBeanPermission "sun.management.ClassLoadingImpl#*[java.lang:type=ClassLoading]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.ClassLoadingImpl#*[java.lang:type=ClassLoading]", "getMBeanInfo";
      permission javax.management.MBeanPermission "sun.management.ClassLoadingImpl#*[java.lang:type=ClassLoading]", "getAttribute";
      permission javax.management.MBeanPermission "sun.management.RuntimeImpl#*[java.lang:type=Runtime]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.RuntimeImpl#*[java.lang:type=Runtime]", "getMBeanInfo";
      permission javax.management.MBeanPermission "sun.management.RuntimeImpl#*[java.lang:type=Runtime]", "getAttribute";
      permission javax.management.MBeanPermission "sun.management.ThreadImpl#*[java.lang:type=Threading]", "queryNames";
      permission javax.management.MBeanPermission "sun.management.ThreadImpl#*[java.lang:type=Threading]", "getMBeanInfo";
      permission javax.management.MBeanPermission "sun.management.ThreadImpl#*[java.lang:type=Threading]", "getAttribute";
      permission javax.management.MBeanPermission "com.sun.management.UnixOperatingSystem#*[java.lang:type=OperatingSystem]", "queryNames";
      permission javax.management.MBeanPermission "com.sun.management.UnixOperatingSystem#*[java.lang:type=OperatingSystem]", "getMBeanInfo";
      permission javax.management.MBeanPermission "com.sun.management.UnixOperatingSystem#*[java.lang:type=OperatingSystem]", "getAttribute";
      permission javax.management.MBeanPermission "org.wso2.carbon.caching.impl.CacheMXBeanImpl#-[org.wso2.carbon:type=Cache,*]", "registerMBean";
      permission javax.management.MBeanPermission "org.apache.axis2.transport.base.TransportView#-[org.apache.synapse:Type=Transport,*]", "registerMBean";
      permission javax.management.MBeanPermission "org.apache.axis2.transport.base.TransportView#-[org.apache.axis2:Type=Transport,*]", "registerMBean";
      permission javax.management.MBeanPermission "org.apache.axis2.transport.base.TransportView#-[org.apache.synapse:Type=Transport,*]", "registerMBean";
      permission java.lang.RuntimePermission "modifyThreadGroup";
      permission java.io.FilePermission "${carbon.home}/repository/database", "read";
      permission java.io.FilePermission "${carbon.home}/repository/database/-", "read";
      permission java.io.FilePermission "${carbon.home}/repository/database/-", "write";
      permission java.io.FilePermission "${carbon.home}/repository/database/-", "delete";
    };
    // Trust all super tenant deployed artifacts
    grant codeBase "file:${carbon.home}/repository/deployment/server/-" {
           permission java.security.AllPermission;
    };
    grant codeBase "file:${carbon.home}/lib/tomcat/work/Catalina/localhost/-" {
     permission java.io.FilePermission "/META-INF", "read";
     permission java.io.FilePermission "/META-INF/-", "read";
     permission java.io.FilePermission "-", "read";
     permission org.osgi.framework.AdminPermission "*", "resolve,resource";
     permission java.lang.RuntimePermission "*", "accessClassInPackage.org.apache.jasper.compiler";
    };
    // ========= Platform signed code permissions ===========================
    grant signedBy "signFiles" {
     permission java.security.AllPermission;
    };
    // ========= Granting permissions to webapps ============================
    grant codeBase "file:${carbon.home}/repository/deployment/server/webapps/-" {
       // Required by webapps. For example JSF apps.
       permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
       // Required by webapps. For example JSF apps require this to initialize com.sun.faces.config.ConfigureListener
       permission java.lang.RuntimePermission "setContextClassLoader";
       // Required by webapps to make HttpsURLConnection etc.
       permission java.lang.RuntimePermission "modifyThreadGroup";
       // Required by webapps. For example JSF apps need to invoke annotated methods like @PreDestroy
       permission java.lang.RuntimePermission "accessDeclaredMembers";
       // Required by webapps. For example JSF apps
       permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.compiler";
       // Required by webapps. For example JSF EL
       permission java.lang.RuntimePermission "getClassLoader";
       // Required by CXF app. Needed when invoking services
       permission javax.xml.bind.JAXBPermission "setDatatypeConverter";
       // File reads required by JSF (Sun Mojarra & MyFaces require these)
       // MyFaces has a fix https://issues.apache.org/jira/browse/MYFACES-3590   
       permission java.io.FilePermission "/META-INF", "read";
       permission java.io.FilePermission "/META-INF/-", "read";
       // OSGi permissions are required to resolve bundles. Required by JSF
       permission org.osgi.framework.AdminPermission "*", "resolve,resource";

    };

Test this use case

We have created a sample web application [3] which we can be used for security tests. With this rest service we can perform basic security tests like file copy, delete, system property read, etc. You need to deploy this in a tenant space and call rest APIs as follows. For sample requests we assume tenant name (domain) is xxx.xxx. If you deploy the sample web application in tenant space you will get a service URL with /t/xxx.xxx/webapps/security-check/. You may invoke operations as follows. With this application you can test file read, write, move, delete, system property read, configuration read and carry out a few other operations as well. It's always recommended to test these operations after you enable security manager.

Requests should be send in the following format:

HTTP GET - Read file (complete file path)

https://test.com/t/xxx.xxx/webapps/security-check/directFile?fileName=re...

HTTP POST - Create file (complete file path)

https://test.com/t/xxx.xxx/webapps/security-check/directFile?fileName=re...

HTTP DELETE - Delete file in Server (complete file path)
https://test.com/t/xxx.xxx/webapps/security-check/directFile?fileName=re...

HTTP GET - Read file (file path from carbon server home)

https://test.com/t/xxx.xxx/webapps/security-check/file?fileName=reposito...

HTTP POST - Create file (file path from carbon server home)

https://test.com/t/xxx.xxx/webapps/security-check/file?fileName=reposito...

HTTP DELETE - Delete file in Server (file path from carbon server home)

https://test.com/t/xxx.xxx/webapps/security-check/file?fileName=reposito...

HTTP GET - Read system property

https://test.com/t/xxx.xxx/webapps/security-check/systemProperty/java.home

HTTP POST - Copy files in server using carbon Utility methods

https://test.com/t/xxx.xxx/webapps/security-check/fileCopy?source=reposi...

HTTP POST - Delete files in server using carbon Utils

https://test.com/t/xxx.xxx/webapps/security-check/fileDelete?path=reposi...

HTTP POST - Get registryDBConfig as string

https://test.com/t/xxx.xxx/webapps/security-check/registryDBConfig

HTTP POST - Get userManagerDBConfig config as string

https://test.com/t/xxx.xxx/webapps/security-check/userManagerDBConfig

HTTP GET - Get network configs as string

https://test.com/t/xxx.xxx/webapps/security-check/networkConfigs

HTTP GET - Get server configuration as string

https://test.com/t/xxx.xxx/webapps/security-check/serverConfiguration

HTTP POST - Get network configs as string

https://test.com/t/xxx.xxx/webapps/security-check/networkConfigs?hostNam...

 

Conclusion

The security manager contributes to the JVM's security model by establishing a custom security policy for Java applications. Moreover, we can define policies according to our requirements. As users can deploy web applications and jaggery applications within the server, they can run their code in the server too. Hence, we need to have some kind of mechanism to control application operations. For example, we can consider file write operations performed by a web application. In such cases, we can use Java security manager to block certain operations. In this article, we discussed how we can apply security policies to WSO2 API Manager. According to your requirement, you can have a more restricted security policy.

 

References

[1] https://docs.oracle.com/javase/7/docs/api/java/lang/SecurityManager.html

[2] https://docs.oracle.com/javase/tutorial/essential/environment/security.html

[3] https://github.com/sanjeewa-malalgoda/test-apps/tree/master/security-check

 

About Author

  • Sanjeewa Malalgoda
  • Director - Engineering | Architect at WSO2
  • WSO2