This tutorial demonstrates the ways you can apply data-level security
to GeoMesa. It is a more advanced tutorial; you should already be familiar
with the basics of GeoMesa and GeoServer. This tutorial targets Accumulo -
GeoMesa also supports HBase visibilities through the same mechanisms, but the
HBase configuration required is not covered here. See HBase Visibilities
for more information on HBase.

In this tutorial, you will learn how to:

Set visibilities on your data during ingestion into GeoMesa

Apply authorizations to your queries through GeoMesa

Implement user authorizations through the GeoMesa GeoServer plugin,
using PKI certs to authenticate with GeoServer and LDAP to store
authorizations

One of the most powerful features of Accumulo is the implementation of
cell-level security, using visibilities and authorizations. Data
that is protected by visibilities can only be seen by users that have
the corresponding authorizations. This allows for the fine-grained
protection of data, based on arbitrary labels.

Note

Authorizations are distinct from table-level
permissions, and operate at a much finer grain.

Public key infrastructure can be used to securely authenticate end
users. In PKI, a certificate authority (CA) will issue digital
certificates that verify that a particular public key belongs to a
particular individual. Other users can then trust that certificate
because it has been digitally signed by the CA.

In this tutorial, the keys used are not provided by trusted CAs. As
such, it is necessary to import the CA’s certificate into the Java
keystore, which allows Java (and by extension Tomcat) to trust any keys
verified by the CA.

PKI solves the issue of authentication (who a user is) but not
authorization (what a user can do). For this tutorial,
authorization is provided by an LDAP server.

This tutorial operates by inserting and then querying several thousand features.
The features are inserted with visibility labels, and then queried with two different users
to show how authorizations work.

When performing a query, GeoMesa delegates the retrieval of
authorizations to service providers that implement the following
interface:

packageorg.locationtech.geomesa.security;publicinterfaceAuthorizationsProvider{/** * Gets the authorizations for the current context. This may change over time * (e.g. in a multi-user environment), so the result should not be cached. * * @return */List<String>getAuthorizations();/** * Configures this instance with parameters passed into the DataStoreFinder * * @param params */voidconfigure(Map<String,Serializable>params);}

When a GeoMesa DataStore is instantiated, it will scan for available
service providers. Third-party implementations can be enabled by placing
them on the classpath and including a special service descriptor file.
See the Oracle
Javadoc
for details on implementing a service provider.

The GeoMesa DataStore will call configure() on the
AuthorizationsProvider implementation, passing in the parameter map
from the call to DataStoreFinder.getDataStore(Mapparams). This
allows the AuthorizationsProvider to configure itself based on the
environment.

To ensure that the correct AuthorizationsProvider is used, GeoMesa
will throw an exception if multiple third-party service providers are
found on the classpath. In this scenario, the particular service
provider class to use can be specified by the following system property:

For simple scenarios, the set of authorizations to apply to all queries
can be specified when creating the GeoMesa DataStore by using the
geomesa.security.auths configuration parameter. This will use the
DefaultAuthorizationsProvider implementation provided by GeoMesa.

If there are no AuthorizationsProviders found on the classpath,
and the geomesa.security.auths parameter is not set, GeoMesa will default to using
the authorizations associated with the underlying Accumulo connection
(i.e. the accumulo.user configuration value).

Warning

This is not a recommended approach for a production system.

In addition, please note that the authorizations used in any scenario
cannot exceed the authorizations of the underlying Accumulo connection.

This tutorial requires that you specify a visibility string and the associated
authorizations string. The visibilities can be anything valid for your Accumulo instance.
For the rest of this exercise, we are going to assume the visibility string is user.
You can see the visibilities that are currently enabled for your user through the Accumulo shell:

The source code is meant to be accessible for this tutorial. The main logic is contained in
org.geomesa.example.accumulo.auths.AuthorizationsTutorial in the
geomesa-tutorials-accumulo/geomesa-tutorials-accumulo-authorizations module. Some relevant methods are:

createDataStore uses a system property to control the visibility provider used by each data store

queryFeatures run the same query with each data store

// get an instance of the data store that uses our authorizations provider,// that always returns empty authsSystem.setProperty(AuthorizationsProvider.AUTH_PROVIDER_SYS_PROPERTY,EmptyAuthorizationsProvider.class.getName());unauthorizedDatastore=super.createDataStore(params);// get an instance of the data store that uses the default authorizations provider,// which will use whatever auths the connector has availableSystem.setProperty(AuthorizationsProvider.AUTH_PROVIDER_SYS_PROPERTY,DefaultAuthorizationsProvider.class.getName());returnsuper.createDataStore(params);

This code snippet shows how you can specify the
AuthorizationProvider to use with a system property. The
DefaultAuthorizationsProvider class is provided by GeoMesa, and used
when no other implementations are found.

The EmptyAuthorizationsProvider class is included in the tutorial. The EmptyAuthorizationsProvider
will always return an empty Authorizations object, which means that any data stored with visibilities
will not be returned.

There is a more useful implementation of AuthorizationsProvider that
will be explored in more detail in the next section, the
LdapAuthorizationsProvider.

Applying Authorizations and Visibilities to GeoServer Using PKIS and LDAP¶

This section will show you how to configure GeoServer to authenticate
users with PKIs, use LDAP to store authorizations, and apply
authorizations on a per-user/per-query basis.

Basic user authentication will take place via user certificates. Each
user will have their own public/private key pair that uniquely
identifies them.

User authorizations will come from LDAP. Once a user’s identity has been
verified via PKI, we will look up the user’s details in LDAP.

Once we have a user’s authentication and authorizations, we will apply
them to the GeoMesa query using a custom AuthorizationsProvider
implementation.

The tutorial code includes an AuthorizationsProvider implementation
that will connect to LDAP to retrieve authorizations, in the class
com.example.geomesa.auths.LdapAuthorizationsProvider.

The provider will configure itself based on the
geomesa-ldap.properties file on the classpath (under
src/main/resources):

# ldap connection propertiesjava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactoryjava.naming.provider.url=ldap://localhost:10389java.naming.security.authentication=simplejava.naming.security.principal=uid=admin,ou=systemjava.naming.security.credentials=secret# the ldap node to start the query fromgeomesa.ldap.search.root=o=Spring Framework# the query that will be applied to find the user's record# the '{}' will be replaced with the common name from the certificate the user has logged in withgeomesa.ldap.search.filter=(&(objectClass=person)(cn={}))# the ldap attribute that holds the comma-delimited authorizations for the usergeomesa.ldap.auths.attribute=employeeType

The default file included with the tutorial will connect to the LDAP
instance we set up in the previous steps. If you are using a different
LDAP configuration, you will need to modify the file appropriately.

The LdapAuthorizationsProvider will look for a particular LDAP
attribute that stores the user’s authorizations in a comma-delimited
list. For simplicity, in this tutorial we have re-purposed an existing
attribute, employeeType. The attribute to use can be modified
through the property file.

When we inserted the ‘rod’ record into LDAP, we set his employeeType
to ‘user,admin’, corresponding to our Accumulo authorizations. If you
are using different authorizations, you will need to update the
attribute to match.

The tutorial code includes a test case for connecting to LDAP, in the
class LdapAuthorizationsProviderTest.

Once you have modified geomesa-ldap.properties to connect to your
LDAP, you can test the connection by running this test class:

In order to use the LdapAuthorizationsProvider, we need to install
it as a service provider into GeoServer, where it will automatically be
picked up by GeoMesa.

The tutorial code includes a service provider registry in the
META-INF/services folder. By default, the provider class is
specified as the EmptyAuthorizationsProvider.

Ensure that your LDAP configuration is correct by running
LdapAuthorizationsProviderTest, as described above.

Change the provider class in the single line file
src/main/resources/META-INF/services/org.locationtech.geomesa.security.AuthorizationsProvider
to be
org.geomesa.example.accumulo.auths.LdapAuthorizationsProvider

Rebuild the tutorial JAR and install the unshaded original jar
in GeoServer: