0

I am attempting to upgrade an application that uses an embedded cassandra 2.1.1 (about time!), but the application in question sets it's own security manager. Cassandra 3.11 seems to not consider this possibility and just attempts to set the security manager on it's own without any consideration that there might already be one (which fails).

2017-06-26T12:05:22,736 ERROR Thread-0 org.apache.cassandra.service.CassandraDaemon Exception encountered during startup
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "createSecurityManager")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) ~[?:1.8.0_101]
    at java.security.AccessController.checkPermission(AccessController.java:884) ~[?:1.8.0_101]
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) ~[?:1.8.0_101]
    at java.lang.SecurityManager.<init>(SecurityManager.java:299) ~[?:1.8.0_101]
    at org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager.<init>(ThreadAwareSecurityManager.java:199) ~[?:3.11.0]
    at org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager.install(ThreadAwareSecurityManager.java:80) ~[?:3.11.0]
    at org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:192) ~[?:3.11.0]
    at org.apache.cassandra.service.CassandraDaemon.activate(CassandraDaemon.java:600) ~[?:3.11.0]
    at org.jesterj.ingest.persistence.Cassandra.start(Cassandra.java:94) ~[?:?]
    at org.jesterj.ingest.Main.startCassandra(Main.java:190) ~[?:?]
    at org.jesterj.ingest.Main.lambda$main$0(Main.java:125) ~[?:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]

There doesn't seem to be any check for configuration to avoid this when I browse the Cassandra code:

public static void install()
{
    if (installed)
        return;
    System.setSecurityManager(new ThreadAwareSecurityManager());

Comments in ThreadAwareSecurityManager seem to indicate that this is meant to make user defined functions safe, but I have no plans to use user defined functions, so I'd be perfectly happy to turn that off, but I don't see such an option in the code.

static
{
    //
    // Use own security policy to be easier (and faster) since the C* has no fine grained permissions.
    // Either code has access to everything or code has access to nothing (UDFs).
    // This also removes the burden to maintain and configure policy files for production, unit tests etc.

This looks suspiciously like it requires a code change to Cassandra before it will ever work. Does anyone have a better idea?

For reference, this comes about as an attempt to escape issues that old cqlsh has with current versions of python:

https://github.com/nsoft/jesterj/issues/89

EDIT: Figured out why the exception occurs despite my previous installation of security manager. It turns out that they install a policy that fails anything not coming from a codesource with a url starting with 'file'. My app loads via one-jar so all my code sources have a url like: onejar:lib/docopt-0.6.1.jar. Thus when they try to install their own security manager, they run afoul of their own policy and die.

Gus
  • 6,719
  • 6
  • 37
  • 58

2 Answers2

1

If possible you could make sure your existing security manager allows it. Maybe something like this is causing issues since it will be applied globally to the JVM.

Alternatively you can just skip it... Its a much worse option and it may break things but you could use reflection.

(inside whatever main class or something loaded very early in application)

static {
        Field installed = ThreadAwareSecurityManager.class.getField("installed");
        installed.setAccessible(true);
        installed.set(null, true);
}

but that may cause issues during runtime so I'd test it throughly.

Chris Lohfink
  • 16,150
  • 1
  • 29
  • 38
  • The code you linked is in the subsection (the webapp) that is presently way obsolete and studiously ignored :). The relevant bit is here https://github.com/nsoft/jesterj/blob/5bd88f1f353ef55dabaeda78437b99f963ff1021/code/ingest/src/main/java/org/jesterj/ingest/Main.java#L263. I think the reflection you suggest leaves the service vulnerable to the UDFs. One thing that puzzles me is in theory I have (for the moment) actually granted all permissions, so I'm slightly shocked I got this error in the first place. – Gus Jun 26 '17 at 21:10
  • Ah figured out why I'm getting this... they install a policy that assumes that the only valid codesource is file:// but since my code is loaded from a one-jar archive the codesource is like this: `onejar:lib/docopt-0.6.1.jar` – Gus Jun 26 '17 at 21:52
  • 1
    can just set `enable_user_defined_functions` to `false` in cassandra.yaml fwiw – Chris Lohfink Jun 26 '17 at 21:54
  • Problem is I'm not sure how to prevent them from setting that policy... – Gus Jun 26 '17 at 21:59
  • Oh duh, I bet I can just touch their class before I set my policy, and install mine before I start them up... – Gus Jun 26 '17 at 22:01
  • Thx this discussion was helpful – Gus Jun 27 '17 at 16:24
1

I got past this particular problem like this:

  1. Set my all perms policy (making sure to also override implies())
  2. Temporarily set a security manager that ignores calls to check permission()
  3. Class.forName("org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager") Which causes the class to initialize and install the offending policy
  4. Set my all perms policy again
  5. Set a vanilla SecurityManager()

Here's the commit: https://github.com/nsoft/jesterj/commit/37c0206eac144e9076600f64bb298039212058f5#diff-6bdddcc8f3676796dbad2f7b694fa7fcL268

This prevents the above stack trace, and leads to a different problem, so I'll end this question here, and open another if needed, but for reference the next problem is still related to ThreadAwareSecurityManager...

java.lang.ClassCastException: org.apache.logging.slf4j.Log4jLogger cannot be cast to ch.qos.logback.classic.Logger
    at org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager.install(ThreadAwareSecurityManager.java:92) ~[cassandra-all-3.11.0.jar:3.11.0]
    at org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:192) ~[cassandra-all-3.11.0.jar:3.11.0]
    at org.apache.cassandra.service.CassandraDaemon.activate(CassandraDaemon.java:600) ~[cassandra-all-3.11.0.jar:3.11.0]
    at org.jesterj.ingest.persistence.Cassandra.start(Cassandra.java:94) ~[main.jar:?]
    at org.jesterj.ingest.Main.startCassandra(Main.java:198) ~[main.jar:?]
    at org.jesterj.ingest.Main.lambda$main$0(Main.java:132) ~[main.jar:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_101]
Gus
  • 6,719
  • 6
  • 37
  • 58