Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 115 additions & 37 deletions apps/fdsnws/descriptions/fdsnws.rst
Original file line number Diff line number Diff line change
Expand Up @@ -442,44 +442,57 @@ Please refer to the documentation of your particular firewall solution on how to
set up this rule permanently.


Authentication Extension
========================
Authentication
==============

The FDSNWS standard requires HTTP digest authentication as the
authentication mechanism. The "htpasswd" configuration option is used to
define the location of the file storing usernames and passwords of users
who are allowed to get restricted data. Any user with valid credentials
would have access to all restricted data.
FDSNWS supports 3 authentication methods for accessing waveform data and
availability of restricted stations/channels:

An extension to the FDSNWS protocol has been developed in order to use
email-pattern-based access control lists, which is an established
authorization mechanism in SeisComP3 (used by Arclink). It works as follows:
* HTTP digest (username/password, FDSNWS standard)
* GPG token (EIDA extension)
* JWT (FDSN proposal)

If ``useAccess`` is set to ``true``, then access to restricted data is
controlled by the :ref:`access` bindings, otherwise any user authenticated with
the HTTP digest method has access to any restricted data.

For security reasons, setting ``useAccess`` to ``true`` is required to access
restricted data if token-based authentication is used.


HTTP digest authentication
--------------------------

In case of standard HTTP digest authentication, the ``htpasswd`` configuration
option is used to define the location of the file storing usernames and
passwords of users.

* The user contacts an authentication service (based on eduGAIN AAI,
e-mail, etc.) and receives a list of attributes (a token), signed by the
authentication service. The validity of the token is typically 30 days.

* The user presents the token to /auth method (HTTPS) of the dataselect
service. This method is the only extension to standard FDSNWS that is
required.
GPG token authentication
-----------------------

* If the digital signature is valid, a temporary account for /queryauth
is created. The /auth method returns username and password of this
account, separated by ':'. The account is typically valid for 24 hours.
* The user contacts an authentication service (based on eduGAIN AAI, e-mail,
etc.) and receives a list of attributes (a token), signed by the
authentication service. The validity of the token is typically 30 days.

* The user presents the token to /auth method (HTTPS) of the dataselect service.
This method is an extension to standard FDSNWS service.

* The username and password are to be used with /queryauth as usual.
* If the digital signature is valid, a temporary account for /queryauth is
created. The /auth method returns username and password of this account,
separated by ':'. The account is typically valid for 24 hours.

* Authorization is based on user's e-mail address in the token and
arclink-access bindings.
* The username and password are to be used with /queryauth as with standard HTTP
digest authentication.


Configuration
-------------
~~~~~~~~~~~~~

The authentication extension is enabled by setting the "auth.enable"
configuration option to "true" and pointing "auth.gnupgHome" to a directory
where GPG stores its files. Let's use the directory
~/seiscomp/var/lib/gpg, which is the default.
The GPG token authentication is enabled by setting ``auth.enable`` to "true"
(this disables standard HTTP digest authentication) and pointing
"auth.gnupgHome" to a directory where GPG stores its files. Let's use the
directory ~/seiscomp/var/lib/gpg, which is the default.

* First create the direcory and your own signing key:

Expand Down Expand Up @@ -509,37 +522,102 @@ where GPG stores its files. Let's use the directory


Usage Example
-------------
~~~~~~~~~~~~~

A client like :ref:`fdsnws_fetch <sec-fdsnws-related>` is recommended, but also tools like wget and
curl can be used. As an example, let's request data from the restricted
station AAI (assuming that we are authorized to get data of this station).
A client like :ref:`fdsnws_fetch <sec-fdsnws-related>` is recommended, but also
tools like wget and curl can be used. As an example, let's request data from the
restricted station AAI (assuming that we are authorized to get data of this
station).

* The first step is to obtain the token from an authentication service.
Assuming that the token is saved in "token.asc", credentials of the
temporary account can be requested using one of the following commands:
Assuming that the token is saved in "token.asc", credentials of the temporary
account can be requested using one of the following commands:

.. code-block:: sh

sysop@host:~$ wget --post-file token.asc https://geofon.gfz.de/fdsnws/dataselect/1/auth -O cred.txt
sysop@host:~$ curl --data-binary @token.asc https://geofon.gfz.de/fdsnws/dataselect/1/auth -o cred.txt

* The resulting file "cred.txt" contains username and password separated by
a colon, so one can conveniently use a shell expansion:
* The resulting file "cred.txt" contains username and password separated by a
colon, so one can conveniently use a shell expansion:

.. code-block:: sh

sysop@host:~$ wget "http://`cat cred.txt`@geofon.gfz.de/fdsnws/dataselect/1/queryauth?starttime=2015-12-15T16:00:00Z&endtime=2015-12-15T16:10:00Z&network=IA&station=AAI" -O data.mseed
sysop@host:~$ curl --digest "http://`cat cred.txt`@geofon.gfz.de/fdsnws/dataselect/1/queryauth?starttime=2015-12-15T16:00:00Z&endtime=2015-12-15T16:10:00Z&network=IA&station=AAI" -o data.mseed

* Using the :ref:`fdsnws_fetch <sec-fdsnws-related>` utility, the two steps above can be combined into
one:
* Using the :ref:`fdsnws_fetch <sec-fdsnws-related>` utility, the two steps
above can be combined into one:

.. code-block:: sh

sysop@host:~$ fdsnws_fetch -a token.asc -s 2015-12-15T16:00:00Z -e 2015-12-15T16:10:00Z -N IA -S AAI -o data.mseed


JWT authentication
------------------

This authentication method is based on JSON Web Token (JWT), which has been
extensively described in RFC 7515 (for signatures), and 7519 (for name
registration and token definition) and it is a global standard for
Authentication and Authorization protocols.

Base64 encoded access token would be included in the HTTP Authorization header
with /query request. /queryauth would not be used in this case.


Configuration
~~~~~~~~~~~~~

To enable JWT authentication, set ``jwt.enable`` to ``true``. In addition you
may want to change the ``jwt.issuers`` and ``jwt.audience`` parameters depending
on which issuers you trust. By default, those are are EIDA (EAS) and EarthScope.

JWT authentication can be used in parallel to standard HTTP digest or GPG token
authentication (but not both, because the latter two are mutually exclusive due
to both using /queryauth).

Usage example
~~~~~~~~~~~~~

* Install the `eas2cli <https://pypi.org/project/eas2cli/>`_ package:

.. code-block:: sh

sysop@host:~$ pip install eas2cli

* Get JWT (it is saved as ~/.eidajwt by default):

.. code-block:: sh

sysop@host:~$ eas2cli login

* Extract the access token:

.. code-block:: sh

sysop@host:~$ token=`python3 -c 'import json; print(json.load(open(".eidajwt"))["access_token"])'`

* Alternatively, get the access token with eas2cli. In this case, the token
would be automatically refreshed if it is expired:

.. code-block:: sh

sysop@host:~$ token=`python3 -c 'from eas2cli.core import gettoken; print(gettoken())'`

* Get data with curl:

.. code-block:: sh

sysop@host:~$ curl -H "Authorization: Bearer $token" 'https://geofon.gfz.de/fdsnws/dataselect/1/query?starttime=2015-12-15T16:00:00Z&endtime=2015-12-15T16:10:00Z&network=IA&station=AAI' -o data.mseed

* Alternatively, a recent version of fdsnws_fetch with JWT support can be used:

.. code-block:: sh

sysop@host:~$ fdsnws_fetch -j ~/.eidajwt -s 2015-12-15T16:00:00Z -e 2015-12-15T16:10:00Z -N IA -S AAI -o data.mseed


Logging
=======

Expand Down
4 changes: 2 additions & 2 deletions apps/fdsnws/descriptions/fdsnws.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@
7232) by the fdsnws-station resource.
</description>
</parameter>
<parameter name="useArclinkAccess" type="boolean" default="false">
<parameter name="useAccess" type="boolean" default="false">
<description>
If enabled, then access to restricted waveform data is
controlled by arclink-access bindings. By default authenticated
controlled by access bindings. By default authenticated
users have access to all data.
</description>
</parameter>
Expand Down
23 changes: 14 additions & 9 deletions apps/fdsnws/fdsnws.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def __init__(self, argc, argv):
self._corsOrigins = ["*"]

self._allowRestricted = True
self._useArclinkAccess = False
self._useAccess = False
self._serveDataSelect = True
self._serveEvent = True
self._serveStation = True
Expand Down Expand Up @@ -538,11 +538,15 @@ def initConfiguration(self):
except Exception:
pass

# use arclink-access bindings
# use access bindings
try:
self._useArclinkAccess = self.configGetBool("useArclinkAccess")
self._useAccess = self.configGetBool("useAccess")
except Exception:
pass
# Backwards compatibility
try:
self._useAccess = self.configGetBool("useArclinkAccess")
except Exception:
pass

# services to enable
try:
Expand Down Expand Up @@ -808,7 +812,7 @@ def initConfiguration(self):
# availability information should be processed
if (
not self._serveEvent
and not self._useArclinkAccess
and not self._useAccess
and (
not self._serveStation
or (not self.isInventoryDatabaseEnabled() and not self._daEnabled)
Expand Down Expand Up @@ -955,7 +959,7 @@ def _site(self):
recordBulkSize : {self._recordBulkSize}
allowRestricted : {self._allowRestricted}
handleConditionalRequests: {self._handleConditionalRequests}
useArclinkAccess : {self._useArclinkAccess}
useAccess : {self._useAccess}
hideAuthor : {self._hideAuthor}
hideComments : {self._hideComments}
evaluationMode : {modeStr}
Expand Down Expand Up @@ -1046,14 +1050,15 @@ def _site(self):
if not retn:
return None

if self._authEnabled:
if self._useAccess or self._authEnabled or self._jwtEnabled:
self._access = Access()

if self._authEnabled:
self._checker = UsernamePasswordChecker(self._userdb)
else:
self._access = Access() if self._useArclinkAccess else None
self._checker = checkers.FilePasswordDB(self._htpasswd, cache=True)

if self._serveDataSelect and self._useArclinkAccess:
if (self._serveDataSelect or _serveAvailability) and self._useAccess:
self._access.initFromSC3Routing(self.query().loadRouting())

seiscomp.datamodel.PublicObject.SetRegistrationEnabled(False)
Expand Down