Plugin jars security policy

I'm working on elasticsearch plugin and currently I cannot hack security policy mechanism.
According to documentation we should avoid disabling security manager, but what if I'm using some jars in my plugin that should have some privileges?

Currently I created such plugin-security.policy file:

grant {
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
permission java.lang.RuntimePermission "getClassLoader";
}

I need this to work with properties file and with Unsafe byte arrays. Customer will have a code source, so for customer development is transparent.

Still I cannot launch my plugin without disabling security manager. What am I doing wrong?

Are you running the code within a specific block as explained here?

// ES permission you should check before doPrivileged() blocks
import org.elasticsearch.SpecialPermission;

SecurityManager sm = System.getSecurityManager();
if (sm != null) {
  // unprivileged code such as scripts do not have SpecialPermission
  sm.checkPermission(new SpecialPermission());
}
AccessController.doPrivileged(
  // sensitive operation
);

Thank you for your answer. I've tried this but I'm not sure that I uderstood this correctly.

The sesentive operation in my case is calling Disruptor ds = new Disruptor(..) which under the hood call Unsafe initialization. I tired to call it as 'priviledged' , but it failed

You need to do something like:

AccessController.doPrivileged(
  Disruptor ds = new Disruptor(..);
);

And make sure that plugin-security.policy is available in your plugins/myplugin directory.
To make sure that it's picked up by elasticsearch, write a malformed policy file and make sure it breaks.

My 2 cents

1 Like

Genius! I spent to much time today digging into this problem and smth went wrong. I saw this code and I even tried it, but it didn't work before, then you wrote it here and voilà it works!

Thank you. Elasticsearch has a great community!

@jaymode @jasontedor
Hi,

I am creating a custom realm plugin for shield and I have used a Jersey client to do a REST call, I am facing the Security Managers Access denied issue.

[2017-02-07 10:56:05,642][INFO ][gateway                  ] [Techno] recovered [1] indices into cluster_state
[2017-02-07 10:56:06,348][INFO ][cluster.routing.allocation] [Techno] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[.kibana][0]] ...]).
[2017-02-07 10:56:09,870][DEBUG][shield.authc             ] [Techno] authentication of request failed for principal [john], uri [/]
java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getClassLoader")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.ClassLoader.checkClassLoaderPermission(ClassLoader.java:1528)
        at java.lang.Thread.getContextClassLoader(Thread.java:1440)
        at com.sun.jersey.spi.service.ServiceFinder.find(ServiceFinder.java:498)
        at com.sun.jersey.api.client.Client.init(Client.java:213)
        at com.sun.jersey.api.client.Client.access$000(Client.java:118)
        at com.sun.jersey.api.client.Client$1.f(Client.java:191)
        at com.sun.jersey.api.client.Client$1.f(Client.java:187)
        at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:193)
        at com.sun.jersey.api.client.Client.<init>(Client.java:187)
        at com.sun.jersey.api.client.Client.<init>(Client.java:159)
        at com.sun.jersey.api.client.Client.create(Client.java:669)
        at org.elasticsearch.example.realm.CustomRealm$1.run(CustomRealm.java:224)
        at org.elasticsearch.example.realm.CustomRealm$1.run(CustomRealm.java:222)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.elasticsearch.example.realm.CustomRealm.authenticate(CustomRealm.java:222)
        at org.elasticsearch.example.realm.CustomRealm.authenticate(CustomRealm.java:61)
        at org.elasticsearch.shield.authc.InternalAuthenticationService.authenticate(InternalAuthenticationService.java:363)
        at org.elasticsearch.shield.authc.InternalAuthenticationService.authenticate(InternalAuthenticationService.java:109)

Following is my code.

	if (userPwJwt != null) {

		if (userPwJwt.length() < 30) {
			byte[] encoded = Base64.encodeBase64((actualUser+":"+userPwJwt).getBytes(StandardCharsets.UTF_8));
			encodedUserPw = "Basic " + new String(encoded,StandardCharsets.UTF_8);
		} else {
			encodedUserPw = "Bearer " + userPwJwt;
		}
		
		SecurityManager sm = System.getSecurityManager();
		if (sm != null) {
		  // unprivileged code such as scripts do not have SpecialPermission
		  sm.checkPermission(new SpecialPermission());
		  
		}
		Client client = AccessController.doPrivileged(new PrivilegedAction<Client>() {
		    public Client run() {
		    	 return Client.create();
		    }});
							 			
		try {
			
			// initiating a client rest request
			WebResource webResource = client.resource(restURL);

			// sending the request and capturing the response
			ClientResponse response = webResource.accept("text/plain").header("Authorization", encodedUserPw)
					.get(ClientResponse.class);

			// check if response code is correct else we return null
			if (response.getStatus() == 200) {
				jwtToken = response.getEntity(String.class);
				if (!jwtToken.equals(null) && !jwtToken.isEmpty()) {
					JWT jwt = JWT.decode(jwtToken);
					Map<String, Claim> claims = jwt.getClaims();

					// mapping the extracted jwt to username & roles
					Claim claimName = claims.get("name");
					resUserName = claimName.asString();
					Claim claimRoles = claims.get("roles");
					resUserRoles = claimRoles.asString().split(",");

					if (resUserName != null && resUserRoles != null) {
						return new User(resUserName, resUserRoles);
					} else {
						logger.debug("Recieved null value from JWT as User:Roles ",
								resUserName + ":" + resUserRoles);
					}
				}
		} 

Is my AccessController method correct? I have changed the security.policy file to allow grant for get class loader.

grant {
  // needed to set custom SSL factory for the CLI migrate tool
  permission java.lang.RuntimePermission "setFactory";
  // Allow RuntimePermission getClassLoader
  permission java.lang.RuntimePermission "getClassLoader";
  permission java.lang.RuntimePermission "accessDeclaredMembers";
};
~

I modified the AccessController to the following, even then i get the same issue:

		SecurityManager sm = System.getSecurityManager();
		if (sm != null) {
		  // unprivileged code such as scripts do not have SpecialPermission
		  sm.checkPermission(new SpecialPermission());
		  logger.info("Have access", "test");			  
		}
		AccessController.doPrivileged(new PrivilegedAction<Void>() {
			
			@Override
		      public Void run() {
		       Client client = Client.create();
		       WebResource webResource = client.resource(restURL);
		       ClientResponse response = webResource.accept("text/plain").header("Authorization", encodedUserPw)
						.get(ClientResponse.class);
		       if (response.getStatus() == 200) {
		    	   ttoken = response.getEntity(String.class);
		       }
		        return null;
			}
		});

Guys I figured this out, thanks !

1 Like

Can you please share how you got over this as I have used your code above and could successfully install the plugin and restart ES, however I get the security error below when I try to invoke my plugin's REST end point.

      "type": "class_not_found_exception",
      "reason": "Provider org.glassfish.jersey.client.JerseyClientBuilder could not be instantiated: java.security.AccessControlException: access denied (\"java.lang.RuntimePermission\" \"getClassLoader\")",
      "caused_by": {
        "type": "access_control_exception",
        "reason": "access denied (\"java.lang.RuntimePermission\" \"getClassLoader\")"
      }```

Sorry for being late, did you get it resolved?