== Introduction ==

This document describes how to get one instance of the Yubikey
Validation Server (YK-VAL) up and running.

The purpose of the Yubikey validation server is to validate Yubikey
OTPs.  The validation server is written in PHP, and thus needs a web
server and a database.  We will use Apache and MySQL, but with small
modifications it should be possible to use with other implementations
too (e.g., lighttpd and PostgreSQL).

The validation server needs to talk to a Yubikey Key Storage Module
(YK-KSM) to work.  Thus, you either need to arrange for access to a
remote YK-KSM and get the URL to it, or install your own YK-KSM.

Currently there are two recommended implementations of a YK-KSM.  If you have a YubiHSM hardware dongle and want improve security, we recommend using Python-PyHSM:

https://github.com/Yubico/python-pyhsm

Otherwise we recommend the "soft" YK-KSM:

https://github.com/Yubico/yubikey-ksm/

The YK-KSM can be on the same machine as the validation server, but
for improved security we recommend to use different machines for the
validation server and the KSM.

The OTP validation service is delivered through web service API.
There's no web-based HTML form interface involved.  The protocol is
defined at:

http://www.yubico.com/developers/api/

For redundancy it is possible to set up multiple instances of the
YubiKey Validation Server.  The intent is that clients should be able
to continue validate OTPs even if one of the servers are down.  This
is reflected in the configuration of a YK-VAL by having a "sync pool"
concept.  The sync pool of a particular YK-VAL instance is a list of
URLs that the local server will synchronize the OTP with, depending on
client request.  Normally if you have 5 servers, each server will have
a list of the 4 other servers in its sync pool -- however it IS
possible to deviate from this if for some reason it is not possible to
reach one particular server from one of the servers.  For simplicity,
we strongly recommend you to list non-local servers in each sync pool
though.

== Installation ==

This following steps applies to any GNU/Linux-like system, although it
was written for Debian GNU/Linux.  If you do not know which OS to use,
we recommend a default choice of Ubuntu 12.04 LTS since it is a
well-known distribution that comes with 5 years of security support.
Install the OS following its manual and enable automatic security
upgrades if prompted.

=== Step 1: YK-VAL Installation ===

First you should download and install the latest YK-VAL release:

....
 user@val:~$ sudo apt-get install git make
 ...
 user@val:~$ git clone git://github.com/Yubico/yubikey-val.git
 ...
 user@val:~$ cd yubikey-val
 user@val:~/yubikey-val$ sudo make install
....

The rest of this documentation will assume you have YK-VAL available
in the default installation targets.  You can override the paths, see
the Makefile.

=== Step 2: Install web server and PHP ===

You also need to install a web server with PHP5, php5-curl and php-pear.

 user@val:~$ sudo apt-get install apache2 php5 php5-curl php-pear

Any web server with PHP support should work.

=== Step 3: Database installation ===

Any SQL database with PHP support should work.  We give examples for
MySQL and PostgreSQL here.  Note that you need to chose between either
PostgreSQL or MySQL here.

==== Step 3A: MySQL Installation ====

Install the required packages:

 user@val:~$ sudo apt-get install mysql-server php5-mysql

The installation asks you for a MySQL "root" password, and I recommend
to specify one. To avoid having to specify a password when using the
'mysql' tool interactively, you can store the password in ~/.my.cnf,
see /usr/share/doc/mysql-server-5.0/README.Debian.gz.  For example:

 user@val:~$ cat > .my.cnf
 [client]
 user = root
 password = YOURPASSWORD
 user@val:~$ chmod go-r .my.cnf
 user@val:~$ 

Note the 'chmod' to protect your password from non-root users.

The database needs to be initialized as follows:

 user@val:~$ echo 'create database ykval' | mysql
 user@val:~$ mysql ykval < /usr/share/doc/yubikey-val/ykval-db.sql 
 user@val:~$ 

You also need to create a database user for the verifier interface,
normally called 'ykval_verifier':

 user@val:~$ mysql --silent ykval
 mysql> CREATE USER 'ykval_verifier'@'localhost'; \
 GRANT SELECT,INSERT,UPDATE(modified, yk_counter, yk_low, yk_high, yk_use, nonce) ON ykval.yubikeys TO 'ykval_verifier'@'localhost'; \
 GRANT SELECT(id, secret, active) ON ykval.clients TO 'ykval_verifier'@'localhost'; \
 GRANT SELECT,INSERT,UPDATE,DELETE ON ykval.queue TO 'ykval_verifier'@'localhost'; \
 SET PASSWORD FOR 'ykval_verifier'@'localhost' = PASSWORD('yourpassword'); \
 FLUSH PRIVILEGES;
 mysql> \q
 user@val:~$ 

==== Step 3B: PostgreSQL Installation ====

Install the required packages:

 user@val:~$ sudo apt-get install postgresql php5-pgsql 
 ...
 user@val:~$ 

The database needs to be initialized as follows:

 user@val:~$ sudo su postgres
 postgres@val:~$ createdb ykval
 postgres@val:~$ psql ykval < /usr/share/doc/yubikey-val/ykval-db.sql 
 postgres@val:~$

You also need to create a database user for the verifier interface,
normally called 'ykval_verifier':

 postgres@val:~$ psql ykval -q
 ykval=# CREATE USER ykval_verifier PASSWORD 'yourpassword'; 
 ykval=# GRANT SELECT,INSERT,UPDATE ON yubikeys TO ykval_verifier; 
 ykval=# GRANT SELECT ON clients TO ykval_verifier; 
 ykval=# GRANT SELECT, INSERT, UPDATE, DELETE ON queue TO ykval_verifier;
 ykval=# \q
 postgres@val:~$ 

Don't forget to switch back to your normal user

 postgres@val:~$ exit
 user@val:~$

During installation and debugging it may be useful to watch the
database log entries:

 user@val:~$ sudo tail -F /var/log/postgresql/postgresql-*-main.log &

=== Step 4: Setup Verify OTP Interface ===

The interface to verify OTPs is implemented using a PHP script.  You
can place the script under any URL, but we recommend serving it as
'http://ykval.example.org/wsapi/verify'.  The simplest way to setup
the symlinks is to invoke 'make symlink' in your YK-VAL source tree.
Like this:

 user@val:~/yubikey-val$ sudo make symlink
 install -d /var/www/wsapi/2.0
 ln -sf /usr/share/yubikey-val/ykval-verify.php /var/www/wsapi/2.0/verify.php
 ln -sf /usr/share/yubikey-val/ykval-sync.php /var/www/wsapi/2.0/sync.php
 user@val:~/yubikey-val$ 

If you want to do it manually, you can invoke the above commands
manually.

=== Step 5: Include path configuration ===

Set the include path for the queue daemon by creating a file
/etc/default/ykval-queue with the following content:

 user@val:~$ sudo sh -c 'cat > /etc/default/ykval-queue'
 DAEMON_ARGS="/etc/yubico/val:/usr/share/yubikey-val"
 user@val:~$ 

You also need to set the include path for the PHP scripts running via
Apache, using a .htaccess file:

 user@val:~$ sudo sh -c 'cat > /var/www/wsapi/2.0/.htaccess'
 RewriteEngine on
 RewriteRule ^([^/\.\?]+)(\?.*)?$ $1.php$2 [L]
 <IfModule mod_php5.c>
  php_value include_path ".:/etc/yubico/val:/usr/share/yubikey-val"
 </IfModule>
 user@val:~$ sudo ln -s 2.0/.htaccess /var/www/wsapi/.htaccess
 user@val:~$

The .htaccess file also sets up rewriting from the non-.PHP suffix URL
name to the right script.

The paths are the default, if you installed the YK-VAL in some other
place you need to modify the paths.

=== Step 6: YK-VAL Configuration ===

You also need to create a ykval-config.php script.  An example file is
included in YK-VAL package as ykval-config.php

A template is typically installed in /etc/yubico/val/ykval-config.php-template.

 user@val:~$ sudo cp /etc/yubico/val/ykval-config.php-template /etc/yubico/val/ykval-config.php
 user@val:~$ sudo emacs -nw /etc/yubico/val/ykval-config.php

Be careful about the user permissions and ownership so that unrelated
users on the system cannot read the database password.

You will typically need to modify the DSN (__YKVAL_DB_DSN__), database
passwords (__YKVAL_DB_PW__), the sync pool lists (__YKVAL_SYNC_POOL__
and __YKVAL_ALLOWED_SYNC_POOL__), and the YK-KSM URLs inside the
otp2ksmurls function.

An example DSN for a MySQL setup: 

 $baseParams['__YKVAL_DB_DSN__'] = "mysql:dbname=ykval;host=127.0.0.1";

An example DSN for a PostgreSQL setup:

 $baseParams['__YKVAL_DB_DSN__'] = "pgsql:dbname=ykval;host=127.0.0.1";

We recommend to add the hosts in YKVAL_SYNC_POOL as entries in '/etc/hosts' to avoid network delays caused by DNS-lookups. For example:

 user@val:~$ sudo sh -c 'cat >> /etc/hosts'
 1.2.3.4 api1.example.com
 2.3.4.5 api2.example.com
 user@val:~$ 


To improve database performance you can use persistent database connection so that each request doesn't require a new connection to be setup. To enable this modify '__YKVAL_DB_OPTIONS__' as follows:

 $baseParams['__YKVAL_DB_OPTIONS__'] = array(PDO::ATTR_PERSISTENT => true); 

=== Step 7: Apache configuration ===

Create an apache web configuration file for the normal HTTP interface
like this:

....
 user@val:~$ sudo sh -c 'cat > /etc/apache2/sites-available/ykval'
 <VirtualHost *:80>
 	ServerName api.example.com
 	ServerAdmin support@example.com

	DocumentRoot /var/www/
	<Directory />
		Options FollowSymLinks
		AllowOverride None
	</Directory>
	<Directory /var/www/>
		Options FollowSymLinks
		AllowOverride All
		Order allow,deny
		allow from all
	</Directory>

	ErrorLog /var/log/apache2/ykval-error.log
	LogLevel warn

	CustomLog /var/log/apache2/ykval-access.log "%h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\""
	ServerSignature On

 </VirtualHost>
 user@val:~$ 
....

HTTPS is strictly speaking not required, but we strongly recommend it.

You need to install a TLS stack for Apache, there are two popular
options here: mod_gnutls and mod_ssl.  We'll explain how to install
both, but you will need to decide which one to use.

You will need to create a key/certificate for your server using normal
tools like GnuTLS "certtool".  A small howto for !GoDaddy is available
from
http://permalink.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/4062.

==== Step 7A: HTTPS via mod_gnutls ====

First install and enable the mod_gnutls module:

 user@val:~$ sudo apt-get install libapache2-mod-gnutls
 user@val:~$ sudo a2enmod gnutls
 Enabling module gnutls.
 Run '/etc/init.d/apache2 restart' to activate new configuration!
 user@val:~$ 

You will need to place the private key in
/etc/ssl/private/api.example.com-key.pem and the certificate chain in
/etc/ssl/private/api.example.com-chain.pem.

Create Apache web configuration files: 

....
 user@val:~$ sudo sh -c 'cat > /etc/apache2/sites-available/ykval-ssl'
 Listen 443
 <VirtualHost *:443>
	ServerName api.example.com
	ServerAdmin support@example.com

	GnuTLSEnable on
	GnuTLSCertificateFile /etc/ssl/private/api.example.com-chain.pem
	GnuTLSKeyFile /etc/ssl/private/api.example.com-key.pem
	GnuTLSPriorities NORMAL

	DocumentRoot /var/www/
	<Directory />
		Options FollowSymLinks
		AllowOverride None
	</Directory>
	<Directory /var/www/>
		Options FollowSymLinks
		AllowOverride All
		Order allow,deny
		allow from all
	</Directory>

	ErrorLog /var/log/apache2/ykval-ssl-error.log
	LogLevel warn

	CustomLog /var/log/apache2/ykval-ssl-access.log "%h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\""
	ServerSignature On

 </VirtualHost>
 user@val:~$ 
....

==== Step 7B: HTTPS via mod_ssl ====

The mod_ssl module is typically installed by default, but you need to
enable it.

 user@val:~$ sudo a2enmod ssl
 Enabling module ssl.
 Run '/etc/init.d/apache2 restart' to activate new configuration!
 user@val:~$ 

You will need to place the private key in
/etc/ssl/private/api.example.com-key.pem and the certificate chain in
/etc/ssl/private/api.example.com-chain.pem.

....
 user@val:~$ sudo sh -c 'cat > /etc/apache2/sites-available/ykval-ssl'
 <VirtualHost *:443>
	ServerName api.example.com
	ServerAdmin support@example.com

	SSLEngine on
	SSLCertificateFile /etc/ssl/private/api.example.com-chain.pem
	SSLCertificateChainFile /etc/ssl/private/api.example.com-chain.pem
	SSLCertificateKeyFile /etc/ssl/private/api.example.com-key.pem

	DocumentRoot /var/www/
	<Directory />
		Options FollowSymLinks
		AllowOverride None
	</Directory>
	<Directory /var/www/>
		Options FollowSymLinks
		AllowOverride All
		Order allow,deny
		allow from all
	</Directory>

	ErrorLog /var/log/apache2/ykval-ssl-error.log
	LogLevel warn

	CustomLog /var/log/apache2/ykval-ssl-access.log "%h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\""
	ServerSignature On

 </VirtualHost>
 user@val:~$ 
....

==== Common Apache Configuration ====

This step is the same for both mod_gnutls and mod_ssl.

 user@val:~$ sudo a2enmod rewrite
 Enabling module rewrite.
 Run '/etc/init.d/apache2 restart' to activate new configuration!
 user@val:~$ sudo a2dissite default
 Site default disabled.
 Run '/etc/init.d/apache2 reload' to activate new configuration!
 user@val:~$ sudo a2ensite ykval ykval-ssl
 Enabling site ykval.
 Enabling site ykval-ssl.
 Run '/etc/init.d/apache2 reload' to activate new configuration!
 user@val:~$ sudo /etc/init.d/apache2 restart
 user@val:~$ 

=== Step 8: Logging ===

The PHP interface uses syslog for logging of incoming requests.  The
facility is LOG_LOCAL0.  To place these messages in a separate file,
you can add the following to /etc/syslog.conf, or if you use rsyslog,
create a file /etc/rsyslog.d/ykval.conf with this content:

 user@val:~$ sudo sh -c 'cat > /etc/rsyslog.d/ykval.conf'
 local0.* -/var/log/ykval.log
 user@val:~$ sudo /etc/init.d/rsyslog restart
 ...
 user@val:~$ 

The '-' before the filename avoids syncing the file after each write,
which is recommended for performance.

The log file can grow large quickly, so it is a good idea to setup
rotation of log files.  Here is an example that rotates the log file
weekly.  Create a file /etc/logrotate.d/ykval like this:

 user@val:~$ sudo sh -c 'cat > /etc/logrotate.d/ykval'
 /var/log/ykval.log {
 	weekly
        dateext
	compress
	missingok
	rotate 9999
	notifempty
	postrotate
		invoke-rc.d rsyslog reload > /dev/null
        endscript
 }
 user@val:~$ 

You may want to modify the default /etc/logrotate.d/apache2, useful
things to add are 'dateext' and 'compress' and change 'rotate' to
something large if you want to retain logs.

=== Step 8.1: Fix default log (optional) ===

Unfortunately, most default syslog configuration, including the
syslog.conf configuration file on Debian, will also log all entries to
/var/log/syslog and/or /var/log/messages.

I am not aware of any way to avoid this without modifying these other
rules.  To avoid YK-VAL log entries in these other files, you must
modify the default rules.  For example, edit the following lines of
/etc/rsyslog.conf (or /etc/syslog.conf if you don't use rsyslog):

....
 *.=debug;\
        auth,authpriv.none;\
        news.none;mail.none     -/var/log/debug
 *.*;auth,authpriv.none          -/var/log/syslog
 *.=info;*.=notice;*.=warn;\
        auth,authpriv.none;\
        cron,daemon.none;\
        mail,news.none          -/var/log/messages
....

Change them into:

....
 *.=debug;\
        auth,authpriv.none;\
        news.none;mail.none;local0.none     -/var/log/debug
 *.*;auth,authpriv.none,local0.none              -/var/log/syslog
 *.=info;*.=notice;*.=warn;\
        auth,authpriv.none;\
        cron,daemon.none;\
        local0.none;\
        mail,news.none          -/var/log/messages
....

Idempotent commands to speed this up:

 user@host:~$ sudo perl -pi -e 's/;auth,authpriv.none/;auth,local0.none,authpriv.none/' /etc/rsyslog.conf
 user@host:~$ sudo perl -pi -e 's/news.none;mail.none/news.none;local0.none;mail.none/' /etc/rsyslog.conf
 user@host:~$ sudo perl -pi -e 's/cron,daemon.none/cron,daemon.none;local0.none/' /etc/rsyslog.conf
 user@host:~$ sudo /etc/init.d/rsyslog restart

=== Step 9: Start Sync Daemon ===

When using yubikey-val in a sync pool, you need to have the ykval-queue 
daemon running to ensure that data is synchronized between the servers in 
the pool. The easiest way of running this is to simply invoke ykval-queue 
in a shell:

 user@val:~$ sudo ykval-queue

However, the recommended approach is to automate running this process in 
the background, by use of an init script or similar. Instructions on doing 
so vary depending on your operating system.

=== Step 10: Sync data from an existing server (optional) ===

If you're adding a new server to an existing pool, you can synchronize all 
YubiKey counter data from one of the existing servers. To do so, the server 
you want to sync from needs to be configured to allow it. Do this by editing 
/etc/yubico/val/ykval-config.php on the existing server, adding the new 
servers IP address to the __YKRESYNC_IPS__ setting. You'll most likely want 
to add the IP to the __YKVAL_ALLOWED_SYNC_POOL__ setting as well. You also 
need to edit this file on the new server, adding the existing server(s) IP
address(es) to __YKVAL_ALLOWED_SYNC_POOL__.

Once these permissions have been configured, you can initiate the full sync 
by running the following command from the new server:

 user@val:~$ ykval-synchronize http://<IP or hostname of existing server>/wsapi/2.0/resync all

=== Step 11: Test it ===

You can test the service by requesting a URL.  Using wget, for
example:

....
 user@val:~$ wget -q -O - 'http://localhost/wsapi/2.0/verify?id=1&nonce=asdmalksdmlkasmdlkasmdlakmsdaasklmdlak&otp=dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
 h=/QVWkl5VlcX+Or1A2b3vOeoLEwI=
 t=2010-05-17T14:48:15Z0355
 otp=dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh
 nonce=asdmalksdmlkasmdlkasmdlakmsdaasklmdlak
 status=NO_SUCH_CLIENT

 user@val:~$ 
....

Naturally, you will need to import client keys into the database for
the verify function to work properly.

=== The End ===

You now have a YK-VAL up and running.  See
https://github.com/Yubico/yubikey-ksm/wiki/ServerHardening on how to
improve security of your system.
