[Geoserver-devel] Pluggable Security

Hi all,

I started writing this email before I realized how long it would be, so I have
copied what I was originally going to say into
http://geoserver.org/display/GEOS/GSIP+25+-+Pluggable+Authentication+Mechanisms
on the wiki. This is just me musing on bugs
(http://jira.codehaus.org/browse/GEOS-1579 and GEOS-2000) so there's not a
specific use case driving it, but I think now (with GS 2 coming up) is a good
time to give some serious forethought to security configuration.

-David Winslow

I have been doing some thinking about pluggable security systems in GeoServer.
(inspired mostly by http://jira.codehaus.org/browse/GEOS-1579) Right now we
have at least two "sections" of GeoServer which are guarded by different
authentication mechanisms, but share a database for authorization. Everything
is configured in an XML file that ends up in a JAR inside a WAR which doesn't
really lend itself to end-user customization. That is, user-configurable
pluggable security systems, as we already have a pretty flexible system with
Acegi. However, right now all that's available to users ("users" at the
moment being those willing to modify web.xml and some properties files by
hand) is the option to assign roles and passwords to users, and to restrict
either layers (if we include Andrea's recent work) or service methods to a
subset of those roles. That is, there are three configuration sections
relevant to security:

<users.properties>
defines user, password, list of roles

<layers.properties>
defines layer, read/write, list of allowed roles

<service.properties>
defines service, operation, list of allowed roles

There's also a suggestion (http://jira.codehaus.org/browse/GEOS-2000) to have
a properties file defining access rules based on url and http method (so we
can restrict operations in the REST API), so I'll add:

<rest.properties>
defines URL, HTTP method, list of allowed roles

What's not at all user-configurable right now is the authentication mechanism
used to find out what user is accessing GeoServer. We just have the following
hardcoded:
for the admin console, use an HTTP cookie, redirecting to a form if the cookie
is not found OR if the user is not in the required role (which is also
hardcoded).
for the OWS services, use HTTP basic auth, returning a 401 error when
permission is denied.
for rest stuff, no authentication is implemented.

So we have GeoServer kind of divided into security 'zones;' the admin console
is one; the OWS services are another, and the REST stuff (or maybe just the
rest) constitutes a third. It might be nice for users to be able to define
these zones themselves, but right now they seem pretty radically different in
structure so that should probably wait until we have enough use cases to
better define them. Each zone has not only its own authentication mechanism,
but also its own type of restriction rules:
admin console: it's just 'you have to be in ROLE_ADMIN'
OWS service: layer+access or service+method -> list of allowed roles
REST API: URL+HTTP method -> list of allowed roles

For a first stab at pluggable authentication mechanisms, maybe it's sufficient
to just let users set the mechanism for each of these zones individually; it
could be as simple as including a set of parameters in web.xml that are
expected to be class names of authentication plugins, something like

<context-param>
  <param-name>OWS_AUTHENTICATOR</param-name>
  <param-value>org.geoserver.acegi.HttpBasicAuthenticator</param-value>
</context-param>

Bean names could work here as well. The default values would just emulate the
current authentication system, and the current properties files would still be
used for the authorization tasks.

In the long run though, it may be that users would like to be able to define
their own zones (example use case: multiple REST plugins are provided that
need to provide access to different clients; so you want to allow two
different authentication mechanisms to clients using different subsets of the
REST API). Since a zone as I've defined it is just a subset of GeoServer's
functionality combined with an authentication mechanism and a type of rule, we
could probably have just a file zones.properties that looks something like:

<zones.properties>
#prefix[,prefix...]=authenticator,filter[config[,config]]
/config/=org.geoserver.acegi.FormAndCookieAuthenticator,org.geoserver.security.AdminOnlyFilter
/ows,/wfs,/wms,/wcs,/wfsv=org.geoserver.acegi.HttpBasicAuthenticator,org.geoserver.security.MethodAndLayerFilter(service.properties,layers.properties)
/rest/=org.geoserver.acegi.HttpBasicAuthenticator,org.geoserver.security.HttpMethodFilter(rest.properties)

Both of the JIRA tasks I linked in this message are marked for backporting to
1.6.x, so adding zones.properties as a required configuration file is not a
great option in the short term (adding just the context parameters to web.xml
is better, since we can more easily code in the default values, but still not
fantastic). But I think that we should try and think about extensibility for
the security configuration as well as the catalog for 2.0.

David Winslow ha scritto:

Hi all,

I started writing this email before I realized how long it would be, so I have copied what I was originally going to say into http://geoserver.org/display/GEOS/GSIP+25+-+Pluggable+Authentication+Mechanisms on the wiki. This is just me musing on bugs (http://jira.codehaus.org/browse/GEOS-1579 and GEOS-2000) so there's not a specific use case driving it, but I think now (with GS 2 coming up) is a good time to give some serious forethought to security configuration.

I read the wiki page a few of times and I'm still a little
confused, so sorry if my comments are off the mark. But here we go.

-David Winslow

I have been doing some thinking about pluggable security systems in GeoServer. (inspired mostly by http://jira.codehaus.org/browse/GEOS-1579)

This jira states:
"Make it possible to use different authentication providers by leveraging a plugin mechanism plus some settings in web.xml so that people can choose to use geoserver built in authentication provider, or JAAS one (in the specific, Tomcat's version, since JAAS authorization setup is container dependent, sigh) or whatever other provider the user may fancy to develop as a module."

It is not at all about the zones, but about being able to pick up the
provider that will authenticate users and give back the roles list,
so something that would act as a replacement for users properties.

This seems to me the most important aspect of a pluggable authentication
mechanism. Is it what you're talking about when you're showing
the example relating to the web.xml configuration?

In my opinion the choice of security authentication mechanism is something that should be stored into the data directory, not in web.xml,
so that you can move it around along with the data dir, and so that you
can change it even in containers that do not unpack the war.

The only thing you have to change in web.xml, should you need to,
is the removal of the Acegi filter altogether... that's something we
could think of making configurable outside of web.xml as well,
it's just a little harder than the rest.

Right now we have at least two "sections" of GeoServer which are guarded by different authentication mechanisms, but share a database for authorization.

The authentication mechanism is one as far as I know, it's the authorization mechanism that's different depending on how
you access GeoServer?

However, right now all that's available to users ("users" at the moment being those willing to modify web.xml and some properties files by hand) is the option to assign roles and passwords to users, and to restrict either layers (if we include Andrea's recent work) or service methods to a subset of those roles. That is, there are three configuration sections relevant to security:

<users.properties>
defines user, password, list of roles

This is authentication, built in mechanism, something I would love to
see as pluggable.

<layers.properties>
defines layer, read/write, list of allowed roles

<service.properties>
defines service, operation, list of allowed roles

>

<rest.properties>
defines URL, HTTP method, list of allowed roles

This is authorization, decomposed in two orthogonal ways, data and services. This could (should) be made pluggable as well, thought
it's probably a bit less important than making authentication pluggable.

What's not at all user-configurable right now is the authentication mechanism used to find out what user is accessing GeoServer. We just have the following hardcoded:
for the admin console, use an HTTP cookie, redirecting to a form if the cookie is not found OR if the user is not in the required role (which is also hardcoded).
for the OWS services, use HTTP basic auth, returning a 401 error when permission is denied.
for rest stuff, no authentication is implemented.

So we have GeoServer kind of divided into security 'zones;' the admin console is one; the OWS services are another, and the REST stuff (or maybe just the rest) constitutes a third. It might be nice for users to be able to define these zones themselves, but right now they seem pretty radically different in structure so that should probably wait until we have enough use cases to better define them. Each zone has not only its own authentication mechanism, but also its own type of restriction rules:
admin console: it's just 'you have to be in ROLE_ADMIN'
OWS service: layer+access or service+method -> list of allowed roles
REST API: URL+HTTP method -> list of allowed roles

Admin console wise, seems like saying unix is not flexible
enough because admins have to be in the ROOT group. What am I
missing? :wink:

For a first stab at pluggable authentication mechanisms, maybe it's sufficient to just let users set the mechanism for each of these zones individually; it could be as simple as including a set of parameters in web.xml that are expected to be class names of authentication plugins, something like

<context-param>
  <param-name>OWS_AUTHENTICATOR</param-name>
  <param-value>org.geoserver.acegi.HttpBasicAuthenticator</param-value>
</context-param>

Bean names could work here as well. The default values would just emulate the current authentication system, and the current properties files would still be used for the authorization tasks.

In the long run though, it may be that users would like to be able to define their own zones (example use case: multiple REST plugins are provided that need to provide access to different clients; so you want to allow two different authentication mechanisms to clients using different subsets of the REST API). Since a zone as I've defined it is just a subset of GeoServer's functionality combined with an authentication mechanism and a type of rule, we could probably have just a file zones.properties that looks something like:

<zones.properties>
#prefix[,prefix...]=authenticator,filter[config[,config]]
/config/=org.geoserver.acegi.FormAndCookieAuthenticator,org.geoserver.security.AdminOnlyFilter
/ows,/wfs,/wms,/wcs,/wfsv=org.geoserver.acegi.HttpBasicAuthenticator,org.geoserver.security.MethodAndLayerFilter(service.properties,layers.properties)
/rest/=org.geoserver.acegi.HttpBasicAuthenticator,org.geoserver.security.HttpMethodFilter(rest.properties)

Here I'm loosing you. The authentication mechanism should be one for
the whole server imho... do you want the console to use the built in
mechanism and the services to use a LDAP server instead? In the long
run I would also love to see the authentication mechanism configurable
from the user interface, like a drop down listing "built-in", "JAAS",
"CAS", "LDAP" and so on, but still be used "server wide".

As for the path based approach above, hum, I'm uncertain.
It seems inspired by the acegi ant based filters defined here in
the Acegi configuration file:
http://svn.codehaus.org/geoserver/trunk/geoserver/main/src/main/java/applicationSecurityContext.xml
but they don't look like a replacement of all the structure that's being
set in place.

Usually with Spring you define the static part of your application,
and there is code in the application that handles the dynamic part
by using lookups.

For example, if I were to create a pluggable authentication system,
I would declare in the applicationSecurityContext.xml a GeoserverAuthenticationManager that would be just a router, and
it would decide what actual authentication system to use by
looking into the spring context (for pluggable authenticators)
and in the configuration (to see which one the user chose to use).
Failing the lookup, it would know how to fall back on the default
authenticator.

What I would see as doable is having pluggable authorization
classes able to handle data, rest and ows authorizations, using
the same approach as above (router class + lookup and default
implementation), so that you can roll your own way to authorize
calls as needed. But it seems to me each zone would retain
its own peculiar way to do the authorization, OWS by parsing
the request and coming up with a service and a method (eventually
a version, the request params), REST by the resource path,
what I see as pluggable is the way you use to associate
those tokens to a list of roles.

Doing the same for the actual filter chains that performs the
cookie handling, sessions integration and such is harder thought,
the Acegi list of filters is critical, you can make it not
work at all by just listing the filters in the wrong order.

Consider how many changes you'd have to
do in order to use Digest authentication in place of the
basic one, you'd have to declare new beans, wire them up
in a different way. Do you really want to expose that to the user?

Also, I don't easily see how the above zones configuration and the Acegi
configuration could work together, do you already have a plan?

Cheers
Andrea

On Tuesday 08 July 2008 12:43:42 Andrea Aime wrote:

David Winslow ha scritto:
> Hi all,
>
> I started writing this email before I realized how long it would be, so I
> have copied what I was originally going to say into
> http://geoserver.org/display/GEOS/GSIP+25+-+Pluggable+Authentication+Mech
>anisms on the wiki. This is just me musing on bugs
> (http://jira.codehaus.org/browse/GEOS-1579 and GEOS-2000) so there's not
> a specific use case driving it, but I think now (with GS 2 coming up) is
> a good time to give some serious forethought to security configuration.

I read the wiki page a few of times and I'm still a little
confused, so sorry if my comments are off the mark. But here we go.

> -David Winslow
>
> I have been doing some thinking about pluggable security systems in
> GeoServer. (inspired mostly by http://jira.codehaus.org/browse/GEOS-1579)

This jira states:
"Make it possible to use different authentication providers by
leveraging a plugin mechanism plus some settings in web.xml so that
people can choose to use geoserver built in authentication provider, or
JAAS one (in the specific, Tomcat's version, since JAAS authorization
setup is container dependent, sigh) or whatever other provider the user
may fancy to develop as a module."

It is not at all about the zones, but about being able to pick up the
provider that will authenticate users and give back the roles list,
so something that would act as a replacement for users properties.

This seems to me the most important aspect of a pluggable authentication
mechanism. Is it what you're talking about when you're showing
the example relating to the web.xml configuration?

Right. This basically amounts to having the different zones completely
defined in code, and referred to by name in some configuration file. I
mentioned putting these in web.xml mostly because the JIRA issue suggested
that's where they should be, but I agree with you about the data dir being a
better place for them.

In my opinion the choice of security authentication mechanism is
something that should be stored into the data directory, not in web.xml,
so that you can move it around along with the data dir, and so that you
can change it even in containers that do not unpack the war.

The only thing you have to change in web.xml, should you need to,
is the removal of the Acegi filter altogether... that's something we
could think of making configurable outside of web.xml as well,
it's just a little harder than the rest.

> Right now we
> have at least two "sections" of GeoServer which are guarded by different
> authentication mechanisms, but share a database for authorization.

The authentication mechanism is one as far as I know, it's the
authorization mechanism that's different depending on how
you access GeoServer?

We use HTTP basic auth for authentication in OWS services, and a 'custom' (not
really, since it's provided by Acegi) form + cookie system for the admin page.
These share the roles database (which is used for authorization, but not
sufficient for determining whether or not to deny a request), but use (very)
different rules for determining which roles are required for a request.
Apologies if I'm misusing terms here, I'm pretty green wrt security
discussions.

> However, right now all that's available to users ("users" at the
> moment being those willing to modify web.xml and some properties files by
> hand) is the option to assign roles and passwords to users, and to
> restrict either layers (if we include Andrea's recent work) or service
> methods to a subset of those roles. That is, there are three
> configuration sections relevant to security:
>
> <users.properties>
> defines user, password, list of roles

This is authentication, built in mechanism, something I would love to
see as pluggable.

> <layers.properties>
> defines layer, read/write, list of allowed roles
>
> <service.properties>
> defines service, operation, list of allowed roles
>
> <rest.properties>
> defines URL, HTTP method, list of allowed roles

This is authorization, decomposed in two orthogonal ways, data and
services. This could (should) be made pluggable as well, thought
it's probably a bit less important than making authentication pluggable.

> What's not at all user-configurable right now is the authentication
> mechanism used to find out what user is accessing GeoServer. We just
> have the following hardcoded:
> for the admin console, use an HTTP cookie, redirecting to a form if the
> cookie is not found OR if the user is not in the required role (which is
> also hardcoded).
> for the OWS services, use HTTP basic auth, returning a 401 error when
> permission is denied.
> for rest stuff, no authentication is implemented.
>
> So we have GeoServer kind of divided into security 'zones;' the admin
> console is one; the OWS services are another, and the REST stuff (or
> maybe just the rest) constitutes a third. It might be nice for users to
> be able to define these zones themselves, but right now they seem pretty
> radically different in structure so that should probably wait until we
> have enough use cases to better define them. Each zone has not only its
> own authentication mechanism, but also its own type of restriction rules:
> admin console: it's just 'you have to be in ROLE_ADMIN'
> OWS service: layer+access or service+method -> list of allowed roles
> REST API: URL+HTTP method -> list of allowed roles

Admin console wise, seems like saying unix is not flexible
enough because admins have to be in the ROOT group. What am I
missing? :wink:

To be pedantic: admins DON'T have to be in the root group; if you divide the
system into different sections you can assign UNIX groups based on what is
needed (ie, groups for database, webserver, etc.). One concept that Tim
Schaub was discussing when we were talking about REST was to have separate
admin permissions at each level of the hierarchy (this is similar in principle
to adding 'a' to the ACLs in the current layer security configuration). It
would probably be quite annoying to deal with, and maybe it's more
configurability than it's worth (ie, the number of users who would actually
benefit are not worth the amount of work it would be to do this, or the UI
clutter that would be added). But if we are talking about massive GeoServer
installations then it's probably at least considering supporting allowing
different groups of admins.

> For a first stab at pluggable authentication mechanisms, maybe it's
> sufficient to just let users set the mechanism for each of these zones
> individually; it could be as simple as including a set of parameters in
> web.xml that are expected to be class names of authentication plugins,
> something like
>
> <context-param>
> <param-name>OWS_AUTHENTICATOR</param-name>
> <param-value>org.geoserver.acegi.HttpBasicAuthenticator</param-value>
> </context-param>
>
> Bean names could work here as well. The default values would just
> emulate the current authentication system, and the current properties
> files would still be used for the authorization tasks.
>
> In the long run though, it may be that users would like to be able to
> define their own zones (example use case: multiple REST plugins are
> provided that need to provide access to different clients; so you want to
> allow two different authentication mechanisms to clients using different
> subsets of the REST API). Since a zone as I've defined it is just a
> subset of GeoServer's functionality combined with an authentication
> mechanism and a type of rule, we could probably have just a file
> zones.properties that looks something like:
>
> <zones.properties>
> #prefix[,prefix...]=authenticator,filter[config[,config]]
> /config/=org.geoserver.acegi.FormAndCookieAuthenticator,org.geoserver.sec
>urity.AdminOnlyFilter
> /ows,/wfs,/wms,/wcs,/wfsv=org.geoserver.acegi.HttpBasicAuthenticator,org.
>geoserver.security.MethodAndLayerFilter(service.properties,layers.properti
>es)
> /rest/=org.geoserver.acegi.HttpBasicAuthenticator,org.geoserver.security.
>HttpMethodFilter(rest.properties)

Here I'm loosing you. The authentication mechanism should be one for
the whole server imho... do you want the console to use the built in
mechanism and the services to use a LDAP server instead? In the long
run I would also love to see the authentication mechanism configurable
from the user interface, like a drop down listing "built-in", "JAAS",
"CAS", "LDAP" and so on, but still be used "server wide".

Okay, it seems that I have been misusing 'authentication mechanism' so far to
refer to ways of getting authentication information from the client, rather
than validating and getting the role list from the credentials provided. It
does seem to make sense to have that database shared across the different
'zones'.

As for the path based approach above, hum, I'm uncertain.
It seems inspired by the acegi ant based filters defined here in
the Acegi configuration file:
http://svn.codehaus.org/geoserver/trunk/geoserver/main/src/main/java/applic
ationSecurityContext.xml but they don't look like a replacement of all the
structure that's being set in place.

Usually with Spring you define the static part of your application,
and there is code in the application that handles the dynamic part
by using lookups.

For example, if I were to create a pluggable authentication system,
I would declare in the applicationSecurityContext.xml a
GeoserverAuthenticationManager that would be just a router, and
it would decide what actual authentication system to use by
looking into the spring context (for pluggable authenticators)
and in the configuration (to see which one the user chose to use).
Failing the lookup, it would know how to fall back on the default
authenticator.

What I would see as doable is having pluggable authorization
classes able to handle data, rest and ows authorizations, using
the same approach as above (router class + lookup and default
implementation), so that you can roll your own way to authorize
calls as needed. But it seems to me each zone would retain
its own peculiar way to do the authorization, OWS by parsing
the request and coming up with a service and a method (eventually
a version, the request params), REST by the resource path,
what I see as pluggable is the way you use to associate
those tokens to a list of roles.

Doing the same for the actual filter chains that performs the
cookie handling, sessions integration and such is harder thought,
the Acegi list of filters is critical, you can make it not
work at all by just listing the filters in the wrong order.

Consider how many changes you'd have to
do in order to use Digest authentication in place of the
basic one, you'd have to declare new beans, wire them up
in a different way. Do you really want to expose that to the user?

Also, I don't easily see how the above zones configuration and the Acegi
configuration could work together, do you already have a plan?

I had in mind the zones configuration replacing the current setup; if
everything is user configurable then you should never need to mess with
settings in the source code. But nothing terribly concrete thus far. I may
have given the wrong impression by listing this as a GSIP on the wiki
(although it is definitely up for discussion).

Cheers
Andrea

!DSPAM:4040,4873994532951804284693!

David Winslow ha scritto:
...

We use HTTP basic auth for authentication in OWS services, and a 'custom' (not really, since it's provided by Acegi) form + cookie system for the admin page. These share the roles database (which is used for authorization, but not sufficient for determining whether or not to deny a request), but use (very) different rules for determining which roles are required for a request. Apologies if I'm misusing terms here, I'm pretty green wrt security discussions.

Hum, no, you're right, I'm mixing two things in authentication myself too. We have three moving parts:
- the authentication backend: validates the user credentials, returns
   the user authorities (roles)
- the authentication protocol: the way we get to know the user
   crendentials (basic authentication, digest, cookies, form)
- the authorization mechanism: given a "resource" and a user decides
   whether he can access to it and how

Admin console wise, seems like saying unix is not flexible
enough because admins have to be in the ROOT group. What am I
missing? :wink:

To be pedantic: admins DON'T have to be in the root group; if you divide the system into different sections you can assign UNIX groups based on what is needed (ie, groups for database, webserver, etc.). One concept that Tim Schaub was discussing when we were talking about REST was to have separate admin permissions at each level of the hierarchy (this is similar in principle to adding 'a' to the ACLs in the current layer security configuration). It would probably be quite annoying to deal with, and maybe it's more configurability than it's worth (ie, the number of users who would actually benefit are not worth the amount of work it would be to do this, or the UI clutter that would be added). But if we are talking about massive GeoServer installations then it's probably at least considering supporting allowing different groups of admins.

Ok... that's something that came up when talking about per layer security, that is, the abilitity to have a different admin per namespace (folders and maps, in the new config system ideas), which could be interesting if you have a big install shared by different customers (think ISP).
This could be done by adding a new kind of "action", that is, not only
read and write, but also admin (or more granular, admin-create, admin-update, admin-delete).

Service wise, it seems weird to have someone handling WFS and someone else handling WMS over the same data... can you imagine any real world
application of this?

Here I'm loosing you. The authentication mechanism should be one for
the whole server imho... do you want the console to use the built in
mechanism and the services to use a LDAP server instead? In the long
run I would also love to see the authentication mechanism configurable
from the user interface, like a drop down listing "built-in", "JAAS",
"CAS", "LDAP" and so on, but still be used "server wide".

Okay, it seems that I have been misusing 'authentication mechanism' so far to refer to ways of getting authentication information from the client, rather than validating and getting the role list from the credentials provided. It does seem to make sense to have that database shared across the different 'zones'.

Yep, the authentication backend should be one for the whole install.
But I see your point about having a different mechanism per zone.

...

I had in mind the zones configuration replacing the current setup; if everything is user configurable then you should never need to mess with settings in the source code. But nothing terribly concrete thus far. I may have given the wrong impression by listing this as a GSIP on the wiki (although it is definitely up for discussion).

I understand it user wise, I have a harder time thinking about implementing it still using Acegi. The two items you are listing do replace the authentication protocol setup and the authorization mechanism, so trying to draw a parallel:

/config/=org.geoserver.acegi.FormAndCookieAuthenticator,org.geoserver.security.AdminOnlyFilter

should replace the current:

/**=httpSessionContextIntegrationFilterWithASCTrue,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,consoleExceptionTranslationFilter,filterInvocationInterceptor

where
httpSessionContextIntegrationFilterWithASCTrue,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,consoleExceptionTranslationFilter (and all the associated beans registered in the context)
  --> org.geoserver.acegi.FormAndCookieAuthenticator

and:
filterInvocationInterceptor (and all the beans attached to it)
--> org.geoserver.security.AdminOnlyFilter
(well, sort of, since filterInvocationInterceptor handles all the paths in fact)

This looks like something not trivial to achieve in practice.
A GSIP that is positively voted is usually implemented as proposed, it
implies having not only the good ideas but also the manpower to turn
it into real code. Do we have some significant resources to invest on it?

Cheers
Andrea

Hi all,

I had a chat with Andrea in the IRC channel about this proposal, and decided
it's not really worth a GSIP at this point. The new plan is to continue
discussion on an RnD page
(http://geoserver.org/display/GEOS/Pluggable+Authentication+Mechanisms) and
hopefully come up with a proposal that is implementable in smaller pieces
since there is no big sponsor for this functionality.

-David