GSIP 19 - Per layer security
Overview
Allowing per layer security to complete the security offering started with service layer security
Motivation
Proposal
Backwards Compatability
Feedback
Voting
Links
Proposed By
Assigned to Release
This proposal will be implemented by 1.7.0-rc1 release
State
Under Discussion, In Progress, Completed, Rejected, Deferred
Motivation
At the moment GeoServer only sports OGC service and service method security, many real world use cases need more granular control based on the data contents, and thus a layer based security.
This proposal handles layer wide security only, per feature or per attribute security is out of the scope.
Proposal, user view
This project is to enhance the security subsystem in GeoServer to allow namespace-based and layer-based authorization for read/write access. The description of this work uses the term 'layer' to refer to either a GeoServer feature type or coverage.
Access permissions will be configurable by entries in a the GEOSERVER_DATA_DIR/security/layers.properties property file, following the syntax already available for service level security:
namespace.layer.permission=ROLE_1[,ROLE_2][, ...]]
where:
- namespace indicates the name of the namespace. The wildcard * is used to indicate all namespaces (i.e. global scope).
- layer is the name of a feature type or coverage. The wildcard * is used to indicate all layers in the namespace.
- permission indicates the type of access permission
- 'r' - indicates read access permission
- 'w' - indicates write access permission
- ROLE#_ are the names of roles (defined in the user.properties file). The wildcard * is used to indicate the permission is applied to all users, including anonymous users.
Rules:
- Each entry must have a unique combination of namespace, layer, and permission values.
- If a permission at the global level is not specified, global permissions are assumed to allow read/write access, i.e. ..r=* and/or ..w=*
- If a permission for a namespace is not specified, it inherits permissions from the global specification.
- If a permission for a layer is not specified, it inherits permissions from it's namespace specification.
- If a user belongs to multiple roles, the least restrictive permission they inherit will apply.
Once security is in place:
- the web administration tool, as well as all the demos, will only display namespaces and layers that the current user is allowed to see;
- all the OGC services will handle out only the layers the user can see, if the current user does not have write permission, all attempts at writing will fail
To ease up implementation, GeoServer will behave as if the unauthorized layers are unavailable or physically read only. This also allows for better security (throwing an exception about the layer not being accessible discloses the layer is there).
Any attempt to access a secured layer with not enough rights will be logged for administrators to evaluate security threats and for debugging purposes.
Example configuration protecting a single namespace and a single layer
The following entries demonstrate configuring GeoServer so that it is primarily a read-only server.
*.*.r=* *.*.w=NO_ONE private.*.r=TRUSTED_ROLE private.*.w=TRUSTED_ROLE topp.congress_district.w=STATE_LEGISLATORS
in this scenario here is the map of roles to permissions:
| private.* |
topp.* | topp.congress_district | (all other namespaces) |
|
|---|---|---|---|---|
| NO_ONE | (no access) | w | (no access) | w |
| TRUSTED_ROLE | r/w | r | r | r |
| STATE_LEGISLATURES |
(no access) | r | r/w | r |
| (all other users) |
(no access) | r | r | r |
Example configuration for locking down GeoServer
The following entries demonstrate configuring GeoServer so that it is locked down.
*.*.r=TRUSTED_ROLE *.*.w=TRUSTED_ROLE topp.*.r=* army.*.r=MILITAR_ROLE,TRUSTED_ROLE army.*.w=MILITAR_ROLE,TRUSTED_ROLE
in this scenario here is the map of roles to permissions:
| topp.* | army.* | (all other namespaces) |
|
|---|---|---|---|
| TRUSTED_ROLE | r/w | r/w | r/w |
| MILITAR_ROLE | r | r/w | (no access) |
| (all other users) | r | (no access) | (no access) |
Example Configuration for something more complex
The following entries demonstrate configuring GeoServer with global-, namepace-, and layer-level permissions.
*.*.r=TRUSTED_ROLE *.*.w=NO_ONE topp.*.r=* topp.states.r=USA_CITIZEN_ROLE,LAND_MANAGER_ROLE,TRUSTED_ROLE topp.states.w=NO_ONE topp.poly_landmarks.w=LAND_MANAGER_ROLE topp.militar_bases.r=MILITAR_ROLE topp.militar_bases.w=MILITAR_ROLE
in this scenario here is the map of roles to permissions:
| topp.states | topp.poly_landmarks | topp.militar_bases | topp.(all other layers) | (all other namespaces) | |
|---|---|---|---|---|---|
| NO_ONE | w | r | (no access) | w | w |
| TRUSTED_ROLE | r | r | (no access) | r | r |
| MILITAR_ROLE | (no access) | r | r/w | r | (no access) |
| USA_CITIZEN_ROLE | r | r | (no access) | r | (no access) |
| LAND_MANAGER_ROLE | r | r/w | (no access) | r | (no access) |
| (all other users) | (no access) | r | (no access) | r | (no access) |
Note: The topp.states.w=NO_ONE is not needed, because this permission would be inherited from the global level, i.e.
*.*.w=NO_ONE
Example of an Invalid Configuration File
The following set of entries would not be valid because the namespace, layer, permission combinations of the entries are not unique.
topp.state.rw=ROLE1 topp.state.rw=ROLE2,ROLE3
Proposal, implementation
The current security integration uses an Acegi based filter to perform authentication, Acegi then puts the result of the authentication in a ThreadLocal variable that can be accessed using SecurityContextHolder.getContext().getAuthentication(). This is already leveraged by the dispatcher, that performs the necessary service level checks.
The idea is to replicate the same checks at the catalog level, by introducing a security wrapper as depicted in the following diagram:

The catalog wrapper would forward all calls to the actual catalog, and then filter out the results comparing them with the actual access level of the current user, in particular:
- by removing all layers that the user is not authorized to see
- by wrapping FeatureTypeInfo if the user is not authorized to write them (coverages are at the moment read only, so this does not apply)
This leaves most of the GeoServer code completely security unaware, making development and maintenance easier compared to a solution that would move the security checks onto the services themselves.
The catch is, in order to wrap the catalog, it must be split into an interface and an implementation. This is possible, and it's going to be introduced along with the new configuration proposal, so this one will be implemented as soon as the new configuration proposal lands on trunk
Relationship with service layer security
GeoServer 1.6.0+ sports service level security as described in the user guide.
The data level security builds on top of it, reusing user and role definitions found in $GEOSERVER_DATA_DIR/security/users.properties.
Each request will be subject to both security checks, according to the following workflow:
- the user gets authenticated using Acegi integration (at the moment, either form or HTTP basic authentication)
- the service level security gets checked by the dispatcher, if access is denied, the request ends
- the service will attempt to access the catalog, each of those attempts will be subjected to data level security restrictions
So, to sum up, in order to successfully execute an OGC service call the user will have to be allowed to execute the requested service method and to access the data specified in the request.
Feedback
This section should contain feedback provided by PSC members who may have a problem with the proposal.
Backwards Compatibility
The proposal has not backwards compatibility issues, the default layers.properties file will be empty, meaning all layers can be accessed by everyone.
Voting
Andrea Aime: +1
Alessio Fabiani: +1
Justin Deoliveira: +1
[~jgarnett]: +1
[~sfarber]:
[~roba]: