Re: RE: [Mx4j-user] RE: mx4j-Bugs-969175: Security and delegation
Eamonn McManus <[email protected]>
| Newsgroups | gmane.comp.java.mx4j.devel |
|---|---|
| Organization | Sun Microsystems |
| Message-ID | <[email protected]> |
Ron,
I am not sure that your point about reusing the same SSL connection is
really that important. If two different connections specify equal
RMIClientSocketFactory objects and connect to the same server, then they
are eligible to share the same RMI connection. The RMI infrastructure
arranges this. Two SslRMIClientSocketFactory objects are always equal,
so this should be true for you. Therefore you should find that you are
only establishing an SSL connection once to a given server, even if you
have several different JMXConnector connections.
The JNDI lookup cost is not great and with a little more effort you can
explicitly do it just once and reuse the retrieved object for every
connection.
Concerning the proposed API changes, we would of course need
corresponding API changes on the server side to specify how to validate
the client's credentials.
Without excluding the possibility of adding this functionality, I'd just
note that it should be possible to do it with today's API. Here's how.
The connector server allows you to specify an MBeanServerForwarder via
the method JMXConnectorServer.setMBeanServerForwarder. The idea is that
this object is a kind of interceptor between the real MBean Server and
the Connector Server:
JMXConnectorServer --> MBeanServer
becomes
JMXConnectorServer --> MBeanServerForwarder --> MBeanServer
MBeanServerForwarder extends MBeanServer, so in a configuration like
this each method of the MBeanServerForwarder could check the client
credentials before forwarding the request to the real MBeanServer.
Something like this:
public class CheckingForwarder implements MBeanServerForwarder {
public CheckingForwarder(MBeanServer mbs) {
this.mbs = mbs;
}
public Object getAttribute(ObjectName on, String attrName)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException {
checkCallerCredentials();
return mbs.getAttribute(on, attrName);
}
...likewise for all the other methods of MBeanServer...
}
The question is obviously how the checkCallerCredentials() method can
get the caller's credentials to check them. You can do this by using
subject delegation. The client uses getMBeanServer(Subject) to specify
the Subject to use, including its credentials. The server can access
this Subject using Subject.getSubject(AccessController.getContext()).
Then it can examine the credentials and throw a SecurityException if
they are not correct. (The details of verifying credentials are up to
you but for example it could involve consulting a password file or doing
a check with LDAP.)
One obvious problem here is that the serialized form of a Subject does
not include its credentials as returned by
Subject.getPrivateCredentials() and Subject.getPublicCredentials(). So
you cannot communicate your credentials in that way. Instead you need a
little hack, which is to encode the credentials inside a Principal in
the Subject. For example, you could define PasswordPrincipal and put
PasswordPrincipal("elderberries") in the Subject to communicate a
password of "elderberries". The checkCallerCredentials() method would
check this, and probably destroy the credentials afterwards so that they
would not be visible to the MBeans being invoked. (Each request will
deserialize a new copy of the Subject with its Principals, so destroying
the PasswordPrincipal for one request doesn't affect subsequent ones.)
Implementing every single method of MBeanServer in the CheckingForwarder
is very tedious, especially because of the "throws" clauses, so a nicer
approach is to use java.lang.reflect.Proxy. I am imagining something
like this:
public class CheckingHandler implements InvocationHandler {
public CheckingHandler(MBeanServer mbs) {
this.mbs = mbs;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("getMBeanServer"))
return mbs;
else if (method.getName().equals("setMBeanServer"))
throw new IllegalArgumentException("MBeanServer already set");
else {
checkCallerCredentials();
return method.invoke(mbs, args);
}
}
private final MBeanServer mbs;
}
MBeanServerForwarder forwarder = (MBeanServerForwarder)
Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(),
new Class[] {MBeanServerForwarder.class},
new CheckingHandler(mbs));
connectorServer.setMBeanServerForwarder(forwarder);
Regards,
--
Éamonn McManus, JSR 255 Spec Lead
Ron Vered wrote:
> Eamonn,
>
> I agree with you that RMI is incomplete with respect to security.
>
> Reading your response I see you are coming from deep understanding of the internals. I am mainly looking at the API and what I can do with it compared with what I am required to do.
>
> The security model (which I think is valid) where a Subject is sent to the other side and this Subject is the delegate does not work for us, for a specific application and we find it too constricting. What we want is to be able to send credentials too.
>
> API wise, consider the following:
>
> JMXConnector cntor = JMXConnectorFactory.connect(url, env);
> MBeanServerConnection connection = cntor.getMBeanServerConnection();
>
> /* bunch of operations with 'connection'
> * could be implemented per request, or per 'connection' object
> * caller does not know
> * authentication done once regardless
> */
>
> MBeanServerConnection conn2 = cntor.getMBeanServerConnection(env2); // env2 has delegate credentials
>
> /* bunch of operations with 'conn2'
> * potentially implemented per 'conn2' instance
> * so authentication is done once (with some reasonable exclusions)
> * caller still unaware of how this works
> */
>
> MBeanServerConnection conn2 = cntor.getMBeanServerConnection(delegateSubject);
>
> /* same thing */
>
> Or you could do this:
>
> JMXConnector cntor = JMXConnectorFactory.connect(url);
> MBeanServerConnection connection = cntor.getMBeanServerConnection(env);
>
> /* bunch of operations with 'connection'
> * could be implemented per request, or per 'connection' object
> * caller does not know
> * authentication done once regardless
> */
>
> MBeanServerConnection conn2 = cntor.getMBeanServerConnection(env2); // env2 has delegate credentials
>
> /* bunch of operations with 'conn2'
> * potentially implemented per 'conn2' instance
> * so authentication is done once (with some reasonable exclusions)
> * caller still unaware of how this works
> */
>
> MBeanServerConnection conn2 = cntor.getMBeanServerConnection(delegateSubject);
>
> /* same thing */
>
> In both cases, there is only one addition to the API, being able to pass in credentials to getMBeanServerConnection().
>
> So, if JMX authentication protocol requires few messages to be exchanged, this authentication is done once for several JMX operations to follow.
>
> Please also consider that a new connection potentially requires TCP + SSL + <transport> + JNDI and TCP + SSL + <transport> + JMX messages to be exchanged before you can do productive work. Where as reusing a connection, requires either no additional messages, or just JMX messages. In addition, in a server to server environment (what we have) could potentially relieve resource utilization and unnecessary network traffic.
>
> Regards,
> Ron.
>
> -----Original Message-----
> From: Eamonn McManus [mailto:[email protected]]
> Sent: Monday, September 06, 2004 5:50 AM
> To: Ron Vered
> Cc: MX4J-Dev (E-mail); Bordet, Simone; Luis Miguel Alventosa
> Subject: Re: [Mx4j-devel] RE: [Mx4j-user] RE: mx4j-Bugs-969175: Security and delegation
>
>
> Ron,
>
> Ron Vered wrote:
>
>>>Here's how we could consider changing the API. Suppose a security
>>>context (subject and/or codebase and/or signers), say "creator", makes a
>>>
>>>JMXConnectorServer and supplies a JMXAuthenticator. Later, a
>>>connection
>>>
>>>arrives, and the JMXAuthenticator returns a Subject for it containing
>>>the Principal "remote". Today, basically both "remote" and "creator"
>>>must have all needed permissions for the reason I detailed. However,
>>>suppose we say that an MBean operation that needs FilePermission is
>>>allowed if EITHER:
>>>(1) both "remote" and "creator" have FilePermission (the current
>>>requirement); OR
>>>(2) "remote" has FilePermission and "creator" has
>>>SubjectDelegationPermission("remote").
>>
>>[Ron] This is OK provided the "remote" is some specified principal
>>class and name, both of the policy writer's choosing and where it can
>>potentially be "*" for all.
>
>
> Right. Check out the spec for SubjectDelegationPermission: <http://java.sun.com/j2se/1.5.0/docs/api/javax/management/remote/SubjectDelegationPermission.html>
> which already allows exactly this.
>
>
>>>Concerning your second request, to be able to have several
>>>authenticated
>>>
>>>Subjects use the same RMI connection without using delegation, I am
>>>not
>>>convinced that this gets you enough, compared with simply opening a
>>>connection per authenticated Subject, to justify the additional
>>>complexity. RMI can share the actual network connections between a pair
>>>
>>>of JVMs anyway -- it doesn't have to open a different TCP/IP
>>>connection
>>>for each RMI connection, except in circumstances (involving socket
>>>factories) where it would be impossible to share the JMX Remote
>>>connection anyway.
>
> >
>
>>[Ron] I don't understand what complexity you are referring to, in
>>addition to passing in Subject argument, have an overload accepting
>>credentials, similar to a connect. Basically, make delegation and
>>connection symmetric. Note that RMI may be able to do this
>>multiplexing in some scenarios, but please also consider load-balanced
>>applications and other transports. You already (wisely) decided to
>>have delegation, so please support it in a symmetric manner, which
>>allows different security models to be implemented.
>
>
> If I understand, you are saying that it's all very well for RMI to share
> connections, but say we are using something like JMXMP which doesn't,
> how do we allow multiple identities to share the same connection then?
> I am not sure what the answer is. JMXMP, like the RMI connector, has an
> authentication step when the connection is established, only. Adding
> the ability to authenticate on every request still doesn't seem very
> different from just establishing a connection per request. Indeed, it
> is likely to be substantially more costly than establishing one
> connection per Subject, as you can do today.
>
> If we want to be able to support challenge/response authentication, then
> we would need a design something like this: The client can contact the
> server at any time, authenticate, and receive a cookie representing the
> authenticated identity. Then, in any subsequent request, it can supply
> the cookie, and the request will automatically be performed using the
> previously-authenticated identity.
>
> The server must be allowed to invalidate the cookie at any time, both
> because a client that authenticated last month isn't necessarily still
> valid, and because we don't want the server to be required to maintain
> client state for an unbounded period of time. This means that when the
> client does a request containing an authentication cookie, it must be
> prepared to get back an error, re-authenticate, and re-issue the request.
>
> In essence, this duplicates what the RMI connector already does. Each
> authenticated connection is represented by a separate RMI object. The
> RMI object-id (a 64-bit securely-random integer) is the cookie. The
> server can unexport the RMI object for a connection at any time,
> whereupon the client must make a new authenticated connection. For the
> RMI connector, I still don't think there is anything to be gained by
> adding support for sharing different authenticated identities over the
> same RMI connection.
>
> Adding such support for other connectors could be interesting. But I
> don't think there is a need for this to be reflected in the generic
> client API. I still think this would lead to the API being
> substantially more complicated than it currently is. I could be wrong,
> though -- do you have an idea of what you would like the changed client
> API to look like?
>
> Regards,
-------------------------------------------------------
This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170
Project Admins to receive an Apple iPod Mini FREE for your judgement on
who ports your project to Linux PPC the best. Sponsored by IBM.
Deadline: Sept. 24. Go here: http://sf.net/ppc_contest.php