A/I Auth Server
An authentication server with an LDAP backend and some advanced features:
- OTP support
- application-specific passwords
- rate limiting and brute force protection
- user partitioning across multiple backends
The authentication server can be queried using an HTTP API. The software includes a PAM module (pam_authclient) that can be used to integrate system services.
Configuration
The authentication server data model is based on the concept of a user account. The server knows how to retrieve user accounts stored in LDAP, but it has to be told the specific details of how to find them and how to map the information there to what it needs.
The server's behavior can be configured with a Python file defining some module-level variables:
-
LDAP_URI
is the LDAP connection string. -
LDAP_BIND_DN
andLDAP_BIND_PW
specify the bind credentials: they should allow the server to read authentication-related attributes on the user objects, and to search the user subtree. -
LDAP_SERVICE_MAP
is a{service: query}
dictionary that defines how to query the LDAP database for user accounts, for each service. -
LDAP_SCHEMA
is a dictionary that describes how to retrieve authentication information from the LDAP object attributes.
Query definition
LDAP queries are meant to return a single user account object from the database using a search operation. They are represented as dictionaries, with the following fields:
-
dn
: specifies the full DN of the object -
base
: specifies a base DN for the search -
filter
: specifies a filter to apply to the search
The dn
and base
attributes are mutually exclusive. In fact,
queries can be of two types:
- If you can know the specific DN of a user account object given its
username, specify the
dn
field. This will run a SCOPE_BASE query for that specific DN. In this case,filter
is optional. - Specify
base
andfilter
together to identify a single object. This will result in a SCOPE_SUBTREE search starting atbase
. In this case thefilter
attribute is required.
On every incoming request, the query fields are subject to string
substitution, using the standard Python syntax of the %
operator,
with the following client-provided variables available:
-
user
: username -
service
: authentication service -
shard
: user shard
So, for example, a mail service could use the following query:
{
'base': 'ou=Accounts,dc=example,dc=com',
'filter': 'mail=%(user)s'
}
or alternatively, if the database structure is simple enough:
{
'dn': 'mail=%(user)s,ou=Accounts,dc=example,dc=com'
}
Schema definition
In order to retrieve authentication information from the LDAP object, the authentication server needs to know which attributes to use. To do so, we use a so-called schema definition (a map of symbolic names to LDAP attributes). The following attribute names are defined:
-
password
: attribute containing the encrypted password. Usually (and by default) this isuserPassword
, a somewhat standard LDAP attribute. Since this attribute is often also used for authentication of the LDAP protocol itself, an eventual{crypt}
prefix is ignored. Passwords should be encrypted with the systemcrypt
method. -
otp_secret
: this attribute should contain the hex-encoded TOTP secret. -
app_specific_password
: attribute (possibly defined more than once) containing an encoded app-specific password. -
shard
: if set, LDAP attribute containing a shard ID for user partitioning.
The LDAP backend module makes some assumptions on the structure of the user database: the most important is that app-specific passwords are attributes of the user object. App-specific passwords should be encoded as colon-separated strings:
service:encrypted_password:comment
Again, the password should be encrypted with the system crypt
method. The comment is a free-form string set by the user to tell the
various credentials apart.
OTP implementation
The authentication server uses a very simple implementation of time-based OTP (TOTP), supporting a single secret per user and without any fancy features such as emergency tokens etc. The reason for this is that TOTP authentication requires just plain read-only access to the user database, while counter-based authentication with proper token revocation is a read-write, locked operation which is more difficult to perform on a LDAP backend.
Usage
Client authentication
Requests to the authentication server can themselves be authenticated, and it may be a good idea to do so in a production environment.
Authserv supports TLS-based authentication using a private CA: in order to perform authentication queries, a client must present an X509 certificate signed by a specific Certification Authority.
This feature is enabled by passing the --ca
, --ssl-cert
and
--ssl-key
options to the server.
The PAM client has options to specify a client certificate.
HTTP API
The authentication server accepts HTTP POST requests at the
/api/1/auth
URL. Requests should contain the following parameters
(with a Content-Type of application/x-www-form-urlencoded
):
-
service
: the service to authenticate for -
username
: username -
password
: password -
otp_token
: a 6-digit TOTP token (optional) -
source_ip
: the remote address of the client connection (optional) -
shard
: server shard (optional)
The response is a simple string, one of:
-
OK
: the authentication request was successful -
ERR_AUTHENTICATION_FAILURE
: the authentication request failed -
ERR_OTP_REQUIRED
: an OTP token is required in order to authenticate, but it was not provided in the request.
If the client application receives an ERR_OTP_REQUIRED
result, it
should prompt the user for an OTP token and retry the authentication
request (including the same username and password) with the
otp_token
field set. The PAM module, for example, can ask the user
for the OTP token interactively, while web applications could display
an interstitial form.
PAM
A PAM module, pam_authclient
, is provided to integrate system
services with the authentication server. It provides only the
account (a no-op) and auth operations.
Example usage:
auth required pam_authclient.so auth_server=127.0.0.1:1616
The module knows about the following options:
-
auth_server=
HOST:PORT specifies the authentication server to talk to. It's possible to specify more than one server, separated by commas, in which case they will be tried in sequence in case of failure. -
ssl_crt=
FILE load the X509 client certificate from FILE (in PEM format). -
ssl_key=
FILE load the X509 certificate key from FILE. -
ca=
FILE should point at the X509 CA certificate, used to verify the authenticity of the server connection: the server certificate must be signed by this CA. -
shard=
ID sets the shard parameter, which may be used by the authentication server to limit authentication to a subset of users.
The service
parameter sent to the auth server will be the PAM service,
it is not possible to override this in the PAM configuration.
NGINX mail_http_auth support
The authentication server can optionally offer an HTTP API compatible
with NGINX's mail_http_auth
module. This is particularly useful in
combination with user partitioning, as the server can use the shard
attribute of the user to direct NGINX to the right backend.
The module understands the following configuration variables:
-
NGINX_AUTH_SERVICE
(default:mail
) is the service that will be used for authentication by this module. -
NGINX_AUTH_PORT_MAP
is a dictionary that maps protocol names to port numbers for the backend connection. The default is{"pop3": 110, "imap": 143}
.