<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">\documentclass[a4paper]{jpconf}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}

\bibliographystyle{iopart-num}

\begin{document}

\title{A VOMS module for the Nginx web server}
\author{A Ceccanti, F Giacomini, E Ronchieri and N Terranova}
\ead{francesco.giacomini@cnaf.infn.it}

\begin{abstract} We present the motivation, the design and some
  implementation hints of a software module for the Nginx web server
  aimed at extracting the attributes of a VOMS-issued Attribute
  Certificate during a client authentication based on an X.509 Proxy
  Certificate. The module enables the use of Nginx as a reverse proxy
  to a Grid service, relieving it from most of the work necessary to
  authenticate a client.
\end{abstract}

\section{Introduction}

Every Grid service needs to authenticate and possibly authorize every
request that it receives. The authentication is historically based on
X.509 Proxy Certificates~\cite{rfc5280}, extended with Attribute
Certificates~\cite{rfc5755} obtained from a VOMS
service~\cite{voms}. An Attribute Certificate is conceptually an
assertion signed by the VOMS service that declares the groups the user
submitting the request belongs to within a Virtual Organization
(e.g. a scientific collaboration) and the possible roles they have
within those groups.

As a consequence, a Grid service must carry out a number of
security-related steps before even starting its own business logic:
\begin{itemize}
\item offer an HTTPS endpoint;
\item perform X.509 certificate-based client authentication;
\item extract the VOMS attributes, on which it could later base an
  authorization decision.
\end{itemize}

The purpose of this work is to factor those three actions out of a
Grid service into a common service-independent module to be run by a
reverse proxy deployed in front of the service. The ideal deployment
model is shown in Figure~\ref{fig:deployment}. If the reverse proxy
and the actual service run in a trusted zone, the communication
between the two can even happen over plain HTTP.

\begin{figure}
  \begin{center}
    \includegraphics[width=.9\textwidth]{deployment}
    \caption{\label{fig:deployment}Deployment model enabled by the
      VOMS module run in a front-end service acting as a reverse proxy
      towards the actual service running as the back-end.}
  \end{center}
\end{figure}

After a first prototype~\cite{nginxvoms-cnafar-2016} was prepared as
part of a master thesis, a properly engineered version has been
developed for production use.

\section{Nginx}

The reverse proxy of choice is Nginx~\cite{nginx}. Nginx is an efficient HTTP and
reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy
server, which serves or proxies a large fraction of the busiest
sites on the Internet.

Nginx has a modular architecture: the core software components provide
a solid and efficient foundation to dispatch events (e.g. from
networking) to one or more worker processes to be asynchronously
managed. Additional functionality is further provided by
\textit{modules} that are linked to the Nginx executable. Modules can
also be loaded dynamically at run time, yet they need to be known at
compile time. As a consequence, the typical way to obtain an Nginx
executable is to compile the corresponding source code, specifying the
exact selection of modules needed for the specific deployment.

To simplify our build and testing setup, we have opted to base our
work on top of the OpenResty~\cite{openresty} distribution of Nginx,
which includes already a selection of useful modules.

However we cannot use the Nginx code as-is, for it is unaware of the
delegation mechanism designed by Globus for the Grid Security
Infrastructure on which all Grid services are based. The delegation
mechanism introduces a control character in the HTTP protocol, between
the SSL/TLS handshake and the transmission of the method. Although we
do not intend to support the delegation mechanism in this work, we
wrote a minimal patch to the Nginx request-parsing code, so that it
can accept that extra character and ignore it if it means ``no
delegation''.

An additional patch has been proposed for Nginx upstream, to enable
the support for proxy certificates via a flag in the configuration
file, after recent versions of OpenSSL~\cite{openssl} have removed the
possibility to enable it via an environment variable. Since proxy
certificates are not widely used outside of the Grid world, the patch
has not been accepted; despite being a more convenient and clearer
solution, the patch is not even applied locally, since there is a
workaround applicable directly in our module's code, as described
below.

An instance of Nginx can be configured (and re-configured) through a
file read by the master process. The configuration file can specify,
among many other things, that Nginx runs in reverse-proxy mode and how
the request should be passed to the upstream server (i.e. the
back-end). The syntax of the configuration file allows for the use of
\textit{variables} (in our case, for example, there would be one
called \texttt{\$voms\_fqans}), whose value is provided by a specific
handler that gets executed when the corresponding variable is used.

\section{Building and testing}

Though the development can happen on any platform, the reference
platform for the deployment at the moment is CentOS 7. Moreover, as
mentioned above, we have elected the OpenResty distribution as the
basis for our own Nginx module. A Docker image combining CentOs 7 and
OpenResty is available both for local builds and for the continuous
integration builds bound to the \textit{git} repository that hosts the
source code.

The Docker image is augmented with the necessary dependencies (such as
the VOMS libraries) and with the elected development tools, mostly
installed through the software collections \textit{Developer Toolset
  7} (to have a compiler that supports C++ 2017 and various
sanitizers) and \textit{LLVM Toolset 7} (to have the code formatter
and the static analyzer).

The testing is based on \texttt{Test::Nginx}~\cite{test::nginx}, a
Perl-based testing framework that comes with OpenResty, that allows
the specification of tests with a declarative syntax. We struggled to
find a way to enable TLS client authentication, but the result is very
satisfactory.

The build and the tests are automatically run every time some code is
pushed to the reference git repository~\cite{module-baltig}.

\section{The VOMS module}

The purpose of the VOMS module is to extract the information available
in the Attribute Certificate embedded in the X.509 Proxy Certificate
used for the authentication of the client and make it available as
Nginx variables. The variables can then be used in the configuration
file to form the request that is passed upstream by the reverse proxy.

The variables correspond very closely to the fields of the
\texttt{voms} data structure found in the API of the VOMS C++
library~\cite{voms-github}. For example:

\begin{description}
\item[\texttt{voms\_user}] The Subject of the End-Entity certificate, used to sign the proxy.
\item[\texttt{voms\_fqans}] A comma-separated list of Fully Qualified Attribute
  Names.
\item[\texttt{voms\_vo}] The name of the Virtual Organization (VO) to which the End Entity belongs.
\item[\texttt{voms\_not\_before}] The date before which the Attribute
  Certificate is not yet valid.
\item[\texttt{voms\_not\_after}] The date after which the Attribute Certificate
  is not valid anymore.
\end{description}

The module consists mainly of the handlers that are called when a
variable is referenced in the configuration file. The information
needed to give a value to the variables comes from invocations of the
VOMS library API; that information is obtained as a by-product of the
validation of the X.509 certificate chain presented by the client,
including the VOMS Attribute Certificate. Since such a validation is
expensive, performing it every time a handler is called is best
avoided and a caching strategy is preferable. The caching can be
applied at multiple levels: for each request, for each connection
(multiple requests can be sent over the same connection), for multiple
connections authenticated with the same client proxy certificate. At
the moment the caching is applied at connection level, but moving it
to the next level is already planned.

As mentioned above, the module also enables the support for proxy
certificates in OpenSSL, which in recent versions of the library is
not available any more through setting an environment variable. This
is done calling the appropriate OpenSSL API functions in a handler
that gets executed at the end of the Nginx configuration phase, at a
time when the SSL certificate store, the data structure containing the
flag that enables the acceptance of proxies, is initialized and
available for manipulation.

\section{Deployment}

Once the module described in this work is linked to the Nginx
executable, in order to deploy a Grid service according to the model
sketched in the Introduction, the typical configuration for an Nginx
instance used as a reverse proxy in front of the actual Grid service
would include directives similar to the following.

{\small
\begin{verbatim}

server {

  listen                  443 ssl;
  ssl_certificate         /certs/cert.pem;
  ssl_certificate_key     /certs/key.pem;
  ssl_client_certificate  /etc/pki/tls/certs/ca-bundle.crt;
  ssl_verify_client       on;
  ssl_verify_depth        100;

  location / {

    proxy_set_header      Voms-User       $voms_user;
    proxy_set_header      Voms-Fqans      $voms_fqans;
    proxy_set_header      Voms-Vo         $voms_vo;
    proxy_set_header      Voms-Not-Before $voms_not_before;
    proxy_set_header      Voms-Not-After  $voms_not_after;

    proxy_pass            http://back-end;
  }
}

\end{verbatim}
}
The service running on the back-end would then receive requests over
plain HTTP and will find among its headers all the VOMS information
needed to apply its own authorization policies.

\section{Conclusions and future work}

In this paper we have shown how an Nginx reverse proxy equipped with
the described module could relieve a Grid service from most of the
work necessary to authenticate a client presenting credentials based
on X.509 Proxy Certificates extended with a VOMS-issued Attribute
Certificate.

The module is ready for production use and is currently part of the
development effort aimed at revising the implementation of the StoRM
service.

The main planned development concerns the improvement of the caching
of the information obtained during the validation of VOMS Attribute
Certificates.

\section*{References}
\bibliography{biblio}

\end{document}
</pre></body></html>