Tuesday, May 12, 2015

Securing Tomcat

Securing Tomcat


Tomcat is a great web server. Though for production there is room for improvement.
There are some basic things that need to be done to make your tomcat secure. Some of them are simple like changing the default shutdown port to a different one. Others are simple but can be easily overlooked, like removing the default demo application and removing the management interface. For a list see: http://www.tomcatexpert.com/blog/2011/11/02/best-practices-securing-apache-tomcat-7.

Tomcat Users

If you want to use the tomcat manager application but what to use users, tomcat supports defining roles and users in the tomcat-users.xml file (see https://tomcat.apache.org/tomcat-7.0-doc/manager-howto.html). An example of this file is:
<tomcat-users>
                <user password="test" roles="manager-gui,manager-status,manager-script,manager-jmx" username="tomcat"/>
                <role rolename="admin"/>
</tomcat-users>
For the most part this is good enough, but what if you do not want your password in clear text? Tomcat supports the option to use MD5 instead of the clear text. To do this in the server.xml file, in the Realm node, you need to add the following:
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" digest="md5" resourceName="UserDatabase"/>

JMX Login

Most applications need a JMX MBean for debugging and monitoring. The JVM supports user name and password in the following way.
To your application you add the following JVM args:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8007
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.password.file={file_location}\jmxremote.password
-Dcom.sun.management.jmxremote.access.file={file_location}\jmxremote.access
You will need to change the ACL of the file for the jmxremote.password else the solution will not work (http://docs.oracle.com/javase/7/docs/technotes/guides/management/agent.html).

The problem is that the file with the password is still open and not encrypted. To fix this we need to implement our own Login Module using JASS (have a look at https://blogs.oracle.com/lmalventosa/entry/jmx_authentication_authorization).
The implement our own login module we need to change the JVM args to:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8007
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.ext.dirs={dir_location}
-Dcom.sun.management.jmxremote.login.config=AMFConfig
-Djava.security.auth.login.config={file_location}\AMF.config
-Dcom.sun.management.jmxremote.access.file={file_location}\jmxremote.access

For the login to work, the user still needs to be in the jmxremote.access file with the proper access level (readonly, readwrite).
We have added two new entries:
java.security.auth.login.config – this entirety will tell the JVM to load the JASS configuration file, for example:
TestConfig {
   plugin.com.tomcat.login.MyLoginModule Requisite passwordFile="\user\abc\jmxremote.access";
};
This will tell the JVM to use the class LoginModule for the JASS login. Since the configuration file can have more than one definition, you need to specific which module to use with: com.sun.management.jmxremote.login.config.
What they don’t write is that you need to create a separate jar that includes your login module (plugin.com.tomcat.login.LoginModule). Once you have this jar you need to put it in the java external folder. Adding it to the classpath is not enough. No we usually don’t have access to the java external folder, so since java 6 you can add another path for the external dir: java.ext.dirs. This will load all default external jars and your login.
An example code for the login module is:
public class MyLoginModule implements LoginModule {

       private Subject subject;
       private CallbackHandler callbackHandler;
       private Map<String, ?> options;
       private JMXPrincipal user;

       private boolean succeeded = false;

       public MyLoginModule() {
       }


       @Override
       public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
              this.subject = subject;
              this.callbackHandler = callbackHandler;
              this.options = options;
              succeeded = false;
       }

       //function for map
       private Map<String,String> getUserPassword(String passwordFile){
              Map<String,String> map = new HashMap<String, String>();
              File file = new File(passwordFile);
              if (file.exists()) {
                     try (BufferedReader buffer = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
                           while (true) {
                                  String line = buffer.readLine();
                                  if (line == null)
                                         break;
                                  String[] fields = line.split("\\s+");
                                  String fileUser = fields[0];
                                  String filePassword = fields[1];
                                  map.put(fileUser, filePassword);
                           }
                     } catch (IOException e) {
                           return map;
                     }
              }
              else{
                     System.out.println("password file not found: " + passwordFile);
              }
              return map;
       }
      
       public boolean validateUser(String passwordFile, String userName, String password) {
              String md5 = EncryptionHelper.md5(password);
              Map<String, String> userPasswords = getUserPassword(passwordFile);
              String filePassword = userPasswords.get(userName);
              if (md5.equals(filePassword)) {
                     return true;
              }
              return false;
       }

       @Override
       public boolean login() throws LoginException {
              String passwordFile = (String) options.get("passwordFile");

              if (callbackHandler == null) {
                     throw new LoginException("Oops, callbackHandler is null");
              }

              Callback[] callbacks = new Callback[2];
              callbacks[0] = new NameCallback("name:");
              callbacks[1] = new PasswordCallback("password:", false);

              try {
                     callbackHandler.handle(callbacks);
              } catch (IOException e) {
                     throw new LoginException("Oops, IOException calling handle on callbackHandler");
              } catch (UnsupportedCallbackException e) {
                     throw new LoginException("Oops, UnsupportedCallbackException calling handle on callbackHandler");
              }

              NameCallback nameCallback = (NameCallback) callbacks[0];
              PasswordCallback passwordCallback = (PasswordCallback) callbacks[1];

              String name = nameCallback.getName();
              String password = new String(passwordCallback.getPassword());
              user = new JMXPrincipal(name);

              if (validateUser(passwordFile, name, password)) {
                     succeeded = true;
                     return succeeded;
              } else {
                     succeeded = false;
                     throw new FailedLoginException("Wrong Login! Incorrect User or Password");
              }
       }

       @Override
       public boolean commit() throws LoginException {
              subject.getPrincipals().add(user);

              return succeeded;
       }

       @Override
       public boolean logout() throws LoginException {
              subject.getPrincipals().remove(user);
              return false;
       }

       @Override
       public boolean abort() throws LoginException {
              return false;
       }
      
}

Debugging

To debug the login you have a few options. The regular attach process will work nicely and you can debug your login. In addition on the server side you can add the following system parameter: -Djava.security.debug=all.
Also on the client side you can run the jconsole from the command line with the following parameter:
Jconsole –debug. This will open a debug window and print the errors from the server side.



Sunday, January 4, 2015

Maven inheritance aggregation

Maven inheritance aggregation

Challenge

The challenge is to create a build system (based on maven of course) that will be delivered to the next layer of development. The difficulty here is that the core team will continue to change the delivery system, and release new versions. We do not want (or would like to limit) the adoption teams to always have to merge when there is a new version.

 

Solution

The solution needs to be implemented in more than one single solution and way.  The standard way of maven is inheritance. Any dependency or plugin that you want to add you put it in the parent pom and everyone can inherit this. The problem is that we do not always want to inherit, and we don’t always want the same functionality for all children. To solve this issue you can put any dependencies or plugins in profiles and then activate them in a child module. This solution is not clean since it is very cumbersome and makes the parent pom very big and hard to manage with all the profiles.

BOM (Bill Of Materials)

Since our project consists of multiple jars and versions we do not want the adoption team to have to update all versions for every new release. In addition if we add a new jar we do not want the adoption team to have to add the new jar to their parent pom.
By creating a bom file (more a less a parent pom), the adoption parent pom does not have to inherit your pom, but can import it (http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html). This will allow the adoption to manager the core product with one import on the dependency.
For example (this is put in the dependencyManagement section):
                                                <dependency>
                                                                <groupId>com.xxx.wsf.buildtools</groupId>
                                                                <artifactId>wsf-bom</artifactId>
                                                                <version>${wsf.tools.version}</version>
                                                                <type>pom</type>
                                                                <scope>import</scope>
                                                </dependency>
This will import all the dependency managements from the wsf-bom file.
Just a reminder in the BOM you only have dependencyManagement and not the dependencies themselves. In addition BOM’s are only for dependency management and not plugin management.
Note: When you have a multi module project, you parent pom needs to be the bom so that it is installed first, else no one is installing it since it is imported in the dependencyManagment and not dependencies.

DepChain

A BOM file is a good start, but what do I do if I have multiple projects that need the actual dependencies and not just the versions (dependency management). If it is only one dependency then we can manually add it to the module, but if it is more than one, or even one but in the future it will grow we would like to import the dependencies themselves and not just the dependency management.
A depchain is a pom file that has the actual dependency list and not just the management. Once you have an artifact that is a depchain you can then import it into any module (this is put in the dependencies section:
                                <dependency>
                                                <groupId>com.xxx.wsf.buildtools</groupId>
                                                <artifactId>wsf-depchain-design</artifactId>
                                                <type>pom</type>
                                                <scope>provided</scope>
                                </dependency>              
This will bring in the actual dependency list into your module, notice that the scope is provided.

 

Mixins

Mixin is an object oriented paradigm to add functionality to a class but not by inheritance (http://en.wikipedia.org/wiki/Mixin). Maven has toyed with the idea of adding mixins, currently it is on the roadmap for maven 4 (https://cwiki.apache.org/confluence/display/MAVEN/Roadmap). All thought it is not released by maven, maven allows you to create plugins that are extensions to maven. So a developer by the name of ohad david has written a mixin extension for maven (https://github.com/odavid/maven-plugins).
What this extension allows you to do is to create an external pom that will be merged into your pom. So with this you can write a set of plugins that will give you added value, and then you import it into any module you need this functionality (you also have the option to move the content of the pom to a mixin.xml so that when you install the mixin it does not run the plugins).
For example we would like to write a set of plugins that depending on the OS it will change the permissions of a file in a folder. The pom that does this is fairly simple. What we would like is the option to move this functionality from the parent pom into anther pom that other projects can use. Just like the BOM allows you to import all dependencies version of a project, a dep chain will allow you import the dependencies them self’s.

Example of mixin: permission per os (the value of the properties will be defined in the calling pom).
The pom of the mixin might look something like:
<properties>
<folder.location/>
<windows.permissions>-r /s</windows.permissions>
<linux.permissions>777</linux.permissions>
</properties>
<profiles>
<profile>
  <id>windows</id>
  <activation>
      <os>
         <family>windows</family>
      </os>
   </activation>
       <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                       <artifactId>maven-antrun-plugin</artifactId>
                      <executions>
                          <execution>
                             <id>run app maven</id>
                                <phase>validate</phase>
                                <configuration>
                                    <target>
                                        <exec dir="${folder.location}" executable="attrib" failonerror="true">
                                            <arg line="${windows.permissions} *.*"/>
                                        </exec>
                                    </target>
                                </configuration>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                         </execution>
                     </executions>
                </plugin>
         </plugins>
    </build>
</profile>
<profile>
     <id>linux</id>
         <activation>
             <os>
                    <family>unix</family>
                </os>
         </activation>
        <build>
            <plugins>
                   <plugin>
                       <groupId>org.apache.maven.plugins</groupId>
                       <artifactId>maven-antrun-plugin</artifactId>
                       <executions>
                            <execution>
                                    <id>set permission</id>
                                    <phase>process-test-resources</phase>
                                    <goals>
                                         <goal>run</goal>
                                   </goals>
                                  <configuration>
                                       <target>
                                            <chmod dir="${folder.location}" perm="${linux.permissions}"/>
                                       </target>
                                </configuration>
                          </execution>
                     </executions>
             </plugin>
        </plugins>
    </build>
</profile>

The usage of this plugin in another pom would then be:
<properties>
                <folder.location>${basedir}/src/test/resources/asm</folder.location>
</properties>
<plugin>
                <groupId>com.github.odavid.maven.plugins</groupId>
                <artifactId>mixin-maven-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                                <phase>process-classes</phase>
                                <mixins combine.children="append">
                                                <mixin>
                                                                <groupId>com.abc</groupId>
                                                                <artifactId>wsf-mixin-files-permission</artifactId>
                                                                <type>mixin</type>
                                                </mixin>
                                </mixins>
                </configuration>
</plugin>                           

Why mixins still don’t make it:

Although mixins do give us a lot of functionality, they do not really wrap a full set of functionality to be used in other poms. For example is you write an assembly mixin, all the extra files for the assembly cannot be put in the mixin but need to be put into the calling pom. So we don’t actually have a full encapsulation solution, but a step in the right direction.

For more information on this see:


Thursday, December 4, 2014

Maven Plugin with Java Endorsed

The Challenge

I need to write a maven plugin that in order for it to work it needs endorsed jars loaded into the java JVM (java.endorsed.dirs). An example of this would be to enhance the jaxb facets (see https://github.com/whummer/jaxb-facets).
A simple search on google will bring you to solutions like using the maven-compiler-plugin (for example: http://www.mindbug.org/2009/02/adding-endorsements-to-mavens-plugins.html). This solution will compile your source will the endorsed. What we need is for our plugin that we just wrote, to run in endorsed mode so that I can use within the plugin the new jabx facets features.

Solution

A simple search for which maven plugins support endorsed we will find plugins like surefire and a few others. What they all have in common is that for them to work you need to use the fork flag. This should give us a hint as to the general solution to the problem. For us to use the endorsed in our plugin we need to spawn a new java process and then we can specify the endorsed jvmarg.

Spawn Process

There are a few ways to spawn a new process in java. The main issue is to get the output of the process running (we need both the stdout and the errorout). Maven has a library to help with this: plexus-utils. In this library you have a class called: CommandLineUtils.So for example you can have the following code:
Commandline cl = new Commandline();
cl.setExecutable(path);
cl.createArg().setValue("-Djava.endorsed.dirs=" + endorsedDir);
cl.createArg().setValue("-cp");
cl.createArg().setValue(classpathList);
cl.createArg().setValue(ForkMyPlugin.class.getName());
CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();
CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
CommandLineUtils.executeCommandLine(cl, stdout, stderr);
String output = stdout.getOutput();
if (output.length() > 0) {
getLog().info(output);
}
String errOutput = stderr.getOutput();
if (errOutput.length() > 0) {
getLog().error(errOutput);
throw new MojoExecutionException(errOutput);
}

Of course the file ForkMyPlugin needs to have a main method to handle the execution of the plugin.

ClassPath

The next issue that we need to deal with is how to create the classpath for the plugin fork code. At first thought you could inject MavenProject and get from there all the dependencies of the project. This will not work since you will not get the dependencies from the plugin pom.xml – instead you will get the plugins of the calling pom.xml. If you realy wan’t to get the dependencies on the plugin pom.xml you need to use pluginArtifacts (@Parameter(defaultValue="${plugin.artifacts}", readonly=true)).
But a simpler way will be to just pass on the current classpath to the fork class. To do this, you need to load the resources from the current jar:
Enumeration<URL> resources = getClass().getClassLoader().getResources("META-INF");
From here it should be easy to transfer from the URL to a file location for the classpath (just don’t forget the delimiter for windows is “;” , while for linux it is “:”).

Last but not least is the length of the classpath. There is a limitation on the commandline for how many dependencies you can have. The simple solution is to remove all dependencies that are not used. Especially the maven dependencies should be set to scope – provided.