MAH README version 1.0.1
You receive a phone call from someone who claims to work with you and asks you about sensitive company information. How can you be sure they are who they say they are?
This system, Mutual Authentication for Humans (MAH), is designed to help. It enables you to verify that the person you are communicating with via unauthenticated communications channels (such as phone) is the person they claim to be and current employee of your organisation.
The system uses the organization's 2 factor authentication system to provide the strongest and most practical assurance that someone is a current employee of that organziation. It allows you to create a short authentication code for another employee. By default, the authentication code is a short (e.g. 6) character hex-string, consisting of letters from a-f and numbers 0-9 and codes have a short lifetime (10 minutes).
The system performs a one way authentication via a authentication code, as illustrated in the following example:
- Bob and Mary are based at different locations and are both members of a large project team.
- The project is confidential and they are under strict instructions not to discuss any details with anyone external to the project team.
- Bob calls Mary to ask some questions about the work Mary is doing on the project.
- Mary doesn't usually speak to Bob on the phone and doesn't recognise his voice.
- Mary logs in to the verification system using her username and her LinOTP PIN + token code.
- Mary searches for Bob and generates an authentication code for him.
- Mary says "Bob, before I discuss this information over the phone, I'd like to verify your identity, so I have authenticated you. Can you please read back the authentication code for me?"
- Bob also logs into the verification system and sees the authentication from Mary and reads back the authentication code for Mary.
At this point, Mary has authenticated Bob, but Bob has not authenticated Mary. To authenticate Mary, Bob needs to create a new and different code that Mary will need to read back to him.
MAH can be installed via packages or manually from source.
The Mutual Authentication for Humans (MAH) requires numerous supporting software packages and services.
- The Python interpreter 'python' - installed by default. (MAH has been tested with both Python versions 2.6 and 2.7)
- A web server (e.g. Apache using WSGI)
- A database (e.g. MySQL)
- The Flask micro-framework, which in turn relies on Werkzeug and Jinja 2 (Important: Jinja2 version must be 2.6 or higher).
- The SQL Alchemy SQL tool kit and Object Relational Mapper.
- SeaSurf, a CSRF protection wrapper for Flask.
- The Python Radius API, pyrad and Radius server which is configured to allow the system running MAH to be a NAS (Network Access Server).
- The Python LDAP API, ldap and (read-only) access to an LDAP directory.
1. Apache and MySQL (if needed) and allow connectivity to port 80/443.
# wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm -P /rootwget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm # sudo rpm -ivh mysql-community-release-el7-5.noarch.rpmsudo rpm -ivh mysql-community-release-el7-5.noarch.rpm # yum install httpd mod_wsgi mysql-server MySQL-python
2. Install Flask, SQL Alchemy, pyrad, and ldap (and dependencies). Additional python packages will be installed by pip
# yum install python-pip python-sqlalchemy
3. Install packages (not available as an rpm) via pip
# pip install pyrad flask flask-sqlalchemy flask-seasurf six pip3
4. Download and extract the source tree from the git repository.
# wget https://<source>.tar.gz # tar xzf <name>.tar.gz # cd mah-*
5. Copy the mah code into the webserver directory.
# mkdir /var/www/wsgi/ # cp -r {mah,mah.wsgi} /var/www/wsgi
6. Install apache config
# cp mah.conf /etc/httpd/conf.d/
7. Add the following line to /etc/httpd/conf.d/wsgi.conf to prevent SElinux alerts, if required.
WSGISocketPrefix /var/run/wsgi
8. Running the following MySQL commands as root (or similar DBA account) will set up the required tables.
mysql> create database authdb; mysql> create user 'auth'@'localhost' identified by 'password'; mysql> grant all on authdb.* to 'auth'@'localhost'; mysql> flush privileges; #useful commands: systemctl start mysqld.service; mysql_secure_installation; select * from authentication;
and this dictates the following configuration:
[database] connect = mysql://auth:password1@localhost/authdb
The folloing SE Linux policy modules are required for the application to initiate LDAP and SMTP connections.
module httpd_wsgi_socket 1.0; require { type httpd_log_t; type httpd_t; class sock_file create; } #============= httpd_t ============== allow httpd_t httpd_log_t:sock_file create; module httpd_ldap_connect 1.0; require { type ldap_port_t; type httpd_t; class tcp_socket name_connect; } #============= httpd_t ============== allow httpd_t ldap_port_t:tcp_socket name_connect; module httpd_smtp_connect 1.0; require { type httpd_t; type smtp_port_t; class tcp_socket name_connect; } allow httpd_t smtp_port_t:tcp_socket name_connect;
Additional configuration of the applciation is recommended, particular the use of HTTPS, before Radius authentication is enabled.
For a complete list of configuration options, please see the :doc:`configuration` page.
Apache errors will be report, by default, in /var/log/http/error_log. Application errors will be reported in the MAH log file or via syslog (see :doc:`configuration` page for details on the logging options).
Running the application manually may provide additional debugging information:
/var/www/wsgi/mah/mah.py
This, by default, will start the application using the builtin Werkzeug http server, on localhost and port 5000 by default (editable in mah.conf)
The configuration for MAH is handled via the standard Python ConfigParser framework, which uses the Microsoft INI file structure.
The location of the configuration file can be specified via the environment variable MAHCONFIG.
Example (for Bourne compatible shells):
export MAHCONFIG=/path/to/config/mah.conf
The following shows an example configuration file:
Example mah.conf
[database] connect = mysql://<username>:<password>@<host>/<database> logsql = False [ldap] size_limit = 50 ;max count of findings paged_size = 10 time_limit = 50 server = ldap://<hostname>:<port> ;for ssl use ldaps: user = admin password = 12345 scope = ou=users, dc=corp, dc=com attributes = uid,cn,title,mail,telephoneNumber,location filter = uid,cn,telephoneNumber attributes = uid,cn,title,mail,telephoneNumber,location id_attribute = uid web_attributes = Username,Full Name,Title,Email,Phone,Location [login] check_password = True use_radius = True radius_dictionary = /usr/share/doc/python-pyrad-1.1/example/dictionary radius_server = <hostname> radius_secret = <password> radius_nas_identifer = 1 radius_nas_ip_address = <ip_address> [authentication] timeout = 600 length = 9 variable_length = True [report] email_from = mah@corp.com email_to = user@corp.com email_subject = MAH suspicious activity report smtp_server = smtp.corp.com [application] host = 0.0.0.0 port = 5000 SESSION_KEY = SomeSort0fRandomStringShouldGoHere? SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True PREFERRED_URL_SCHEME = https SESSION_TIMEOUT = 600 REFRESH = 30 [logging] file_name = ./mah.log file_level = DEBUG syslog_level = INFO syslog_facility = local1 syslog_host = localhost syslog_port = 514
This section provides a description of all the configuration elements, including their use and function. The configuration elements are described in the form section.variable.
The database connection string.
<engine>://<db-user>:<db-password>@<db-host>/<database>
This is the argument provided to the create_engine() function of SQL Alchemy. For more information, see the SQL Alchemy documentation for more information.
Enable logging of SQL statements.
logsql = False
Use this configuration option to enable or disable SQL logging for debugging purposes.
Global limit - sets how many entries the server will examine. Count of staff on a page.
size_limit = 250
Global limit - sets maximum page size. Microsoft Active Directory function.
paged_size = 5
Global limit - sets maximum time limit.
time_limit = 30
The LDAP server URL to connect to. For SSL use ldaps:
ldap://<address>:<port>
Login credentials into LDAP server.
user = admin password = 12345
The LDAP scope in which to perform directory searches.
scope = ou=users, dc=corp, dc=com
The LDAP filter specify final results based on terms.
filter = uid,cn,telephoneNumber
The LDAP attributes specify directory searches.
attributes = cn,uid,title,mail,telephoneNumber,location
The LDAP id_attribute specify search_by_uid attribute for searches.
id_attribute = uid
The LDAP web_attributes specify description of every column in table on web page. Used in the same order as 'ldap.attributes'
web_attributes = Username,Full Name,Title,Email,Phone,Location
A Boolean (True/False) option to determine if passwords should be (required and) checked in order to log in to MAH.
This should always be set to True for production systems..
If set to False, the following log entry will be generated on start up:
WARNING: Password checks are configured NOT to be enforced.
Should the login system authenticate users using Radius. If set to true, the further login.radius_* settings are required.
The radius dictionary to use, the default which is installed with pyrad is generally sufficient.
radius_dictionary = /usr/share/doc/python-pyrad-1.1/example/dictionary
The radius server host name which should be used to authenticate users.
radius_server = <RADIUS_hostname>
The radius shared secret for this application/host to use when communicating with the Radius server.
The host name of the Network Access Server (the server running the MAH application).
radius_nas_identifer = <NAS_identifer>
The IP of the Network Access Server (the server running the MAH application).
radius_nas_ip_address = <NAS_address>
When one user authenticates another, the length of time (in seconds) that the shared secret be visible to both users. It is prudent to have relatively short (minutes, as opposed to hours) authentication timeouts.
timeout = 600
The length of the authentication shared secret.
length = 9
Allow variable length of the authentication shared secret in interval <length/length-3>.
variable_length = true
The from email address to use when send suspicious reports.
email_from = <mutual_email>
The to address to send suspicious reports to for further investigation.
email_to = <admin_email>
The subject to use for suspicious reports. Currently, there is no support for templates/variables, so the subject must be a static string.
email_subject = MAH suspicious activity report
The SMTP email server to use for sending suspicious activity reports.
smtp_server = <SMTP_address>
The host address and port of MAH application.
host = 0.0.0.0 port = 5000
The key used by Flask to encrypt session information stored in the cookie.
See also: Flask Configuration Handling
Controls if the cookie should have the secure flag set.
SESSION_COOKIE_SECURE = True
See also: Flask Configuration Handling
Controls if the cookie should have the httponly flag set.
SESSION_COOKIE_HTTPONLY = True
See also: Flask Configuration Handling
The preferred URL generation scheme, default is http.
PREFERRED_URL_SCHEME = https
See also: Flask Configuration Handling
The period of inactivity (in seconds) after which a user's session will be invalidated and they will be required to log in again to continue.
SESSION_TIMEOUT = 600
Note: if a application.REFRESH is set at a rate lower than SESSION_TIMEOUT, then this may counteract the intended function of this variable. In such a situation the (index) page would continually refresh preventing the TIMEOUT threshold from ever being met.
How often (in seconds) should the index page automatically refresh. This is
REFRESH = 30
The file name of log file (if one is configured).
file_name = ./mah.log
- The level of verbosity of messages logged to file. Valid levels are:
- NONE - disable file logging
- DEBUG - emit debugging level information and higher
- INFO - emit general application information and higher
- WARN - emit application warnings and higher
- ERROR - emit error messages and higher
- CRITICAL - emit critical messages only
file_level = DEBUG
- The level of verbosity of messages logged via syslog. Valid levels are:
- NONE - disable file logging
- DEBUG - emit debugging level information and higher
- INFO - emit general application information and higher
- WARN - emit application warnings and higher
- ERROR - emit error messages and higher
- CRITICAL - emit critical messages only
syslog_level = INFO
The syslog facility to use. For more complete information on syslog facilities, please see the <logging.handlers.SysLogHandler documentation.
syslog_facility = local1
The host to send syslog messages to.
syslog_host = localhost
The UDP port to send syslog messages to.
syslog_port = 514
- The included Makefile includes steps for building the docs. From the repositor root, run make docs
- to build the documentation. Note that this will invoke a build of the application, so you will need all the dependencies installed (see 'Pre-requisite package installation' above).
To avoid installing dependencies locally, you can also use the included build.Dockerfile which will prepare a minimal build environment.
docker build -f build.Dockerfile -t mah-build . docker run -v $PWD:/app mah-build htmldocs