Do You Know What is Running?

The biggest benefit of OSGi is that, if you followed the rules, your system can grow quite smoothly for a very long time. However, there is a cost to this quality, quite often unnecessary cruft gathers in the dark corners of your system. Wouldn’t it be nice to know what your system is really doing? Do you really know all the files that are read and written by your code? Are you aware of all the Internet ports your code is opening? Scared? You should be!

Logging is one answer. However, logging requires the cooperation of the bundle developers. Worse, it depends on the subjective judgement of the developer what they like to see. What we now and then really should see, any abuse of resources, is not really on the mind of the developer when they write their logs. There is no way you can really trust the code to log all potentially dangerous actions. Quite often attacks use code that is assuming the input is not dangerous, it would be quite unexpected to get this logged by the developer. And my major problem with logging: the massive amount of unparseable text that a human needs to wrestle through to find the hidden culprits.

Wouldn’t it be nice to see what is happening without relying on logging & developers? Well, there is a way!

One of the odd things in Java is the security manager. It is a quite expensive mechanism based on the mistaken believe that a process can police itself. (I believed it initially as well.) The idea is that every method in Java that requires to check a permission, calls the current Security Manager checkPermission(Permission) method. The Permission parameter is an object that describes the requested permission. For example, a FilePermission contains the action (read,write), and the path, e.g. /etc/passwd. Although I do not think a lot of Java users are using the security model, it is quite expensive performance wise, it actually is complete and well tested. One of the largest Java users, IBM, relies (or at least relied) on it.

The current Security Manager is null when no Security Manager is set.

The Security Manager is a surprisingly interesting place to trace your application. All calls that interact with the outside world pass through this Security Manager. File access, Internet access, property access, etc. Even all OSGi access on services and Framework features are checked. How can we leverage this?

Actually, you only need a few lines of code for a JAR on the bnd -runpath, this is the normal classpath for the bnd launcher. If we give such a JAR an Embedded Activator it will get activated before we start any bundle, thereby being able to capture all permission checks that the bundles cause. An Embedded Activator is identical to a normal Bundle Activator. The difference is that a Bundle Activator runs inside the OSGi framework and an Embedded Activator is activated before the bundles are started.

So what we need is a subclass of the SecurityManager class that overrides the checkPermission(Permission) method. This class can then store all permissions. We should store them as strings in a set. A common problem in OSGi is that you store actual objects, you might prevent important parts from being garbage collected. A string prevents this. We store it in a set so we remove duplicates. If we use a Tree Set, we get the permissions sorted so that the same permission types will be adjacent.

If we store the traced permissions in a set, we need a way to view this set. Well, you knew Gogo was coming … So we add a Gogo command that displays this set.

I’ve created a short video that shows how to make this command with Bndtools.

biz.aQute.osgi.spy.runpath.Spy file:
    package biz.aQute.osgi.spy.runpath;

    import java.security.Permission;
    import java.util.Hashtable;
    import java.util.TreeSet;
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;

    public class Spy implements BundleActivator {

        @Override
        public void start(BundleContext context) throws Exception {
            SecurityManager sm = new SecurityManager() {
                final TreeSet<String>			permissions = new TreeSet<>();

                @Override
                public void checkPermission(Permission perm) {
                    permissions.add( perm.toString());
                }

                @SuppressWarnings("unused")
                public TreeSet<String> permissions() {
                    return permissions;
                }
            };

            Hashtable<String, Object> properties = new Hashtable<>();
            properties.put( "osgi.command.function", new String[] {"permissions"});
            properties.put( "osgi.command.scope", "spy");
            context.registerService(SecurityManager.class, sm, properties);
            System.setSecurityManager(sm);
        }

        @Override
        public void stop(BundleContext context) throws Exception {
            // TODO Auto-generated method stub
        }
    }

bnd.bnd file:
    Embedded-Activator: biz.aQute.osgi.spy.runpath.Spy
    -buildpath: osgi.org;version=6.0.0

test.bndrun file:
    -runpath: biz.aQute.osgi.spy.runpath

You should then edit the file in the Bndrun editor Run tab. You should set the Framework & Execution Environment. Then Resolve & Run. You then get the following output:

("java.io.FilePermission" "/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/content-types.properties" "read")
("java.io.FilePermission" "/Ws/aQute/biz.aQute.osgi.util/biz.aQute.osgi.spy.runpath/etc/gosh_profile" "read")
("java.io.FilePermission" "/Ws/aQute/biz.aQute.osgi.util/biz.aQute.osgi.spy.runpath/target/launch2046327562220408121.properties" "read")
("java.lang.RuntimePermission" "accessDeclaredMembers")
("java.lang.RuntimePermission" "getClassLoader")
...
("java.util.PropertyPermission" "java.net.ftp.imagepath.tar" "read")
("java.util.PropertyPermission" "java.net.ftp.imagepath.text" "read")
("java.util.PropertyPermission" "java.net.ftp.imagepath.tiff" "read")
...
("org.osgi.framework.AdminPermission" "(id=0)" "class,resolve")
("org.osgi.framework.AdminPermission" "(id=0)" "context")
("org.osgi.framework.AdminPermission" "(id=3)" "resolve,resource")
("org.osgi.framework.ServicePermission" "(service.id=10)" "get")
("org.osgi.framework.ServicePermission" "(service.id=13)" "get")
...
("org.osgi.framework.ServicePermission" "aQute.launcher.Launcher" "register")
("org.osgi.framework.ServicePermission" "java.lang.Object" "register")

Since we’re only running Gogo, there is not that much to see. However, run it in your real-world application and you likely be surprised.

And here the video that makes this little gem:

@pkriens


Comments