== 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
https://developers.yubico.com/python-pyhsm/[Python-PyHSM].

Otherwise we recommend the "soft"
https://developers.yubico.com/yubikey-ksm/[YK-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:

[source, sh]
----
user@val:~$ sudo apt-get install git make
...
user@val:~$ git clone git://developers.yubico.com/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.

[source, sh]
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:
 
[source, sh]
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:

[source, sh]
----
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:

[source, sh]
----
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':

[source, sh]
----
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:

[source, sh]
----
user@val:~$ sudo apt-get install postgresql php5-pgsql 
...
user@val:~$ 
----

The database needs to be initialized as follows:

[source, sh]
----
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':

[source, sh]
----
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

[source, sh]
----
postgres@val:~$ exit
user@val:~$
----

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

[source, sh]
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:

[source, sh]
----
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:

[source, sh]
----
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:

[source, sh]
----
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.

[source, sh]
----
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: 

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

An example DSN for a PostgreSQL setup:

[source, php]
$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:

[source, sh]
----
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:

[source, php]
$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:

[source, sh]
----
user@val:~$ sudo sh -c 'cat > /etc/apache2/sites-available/ykval.conf'
<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:

[source, sh]
----
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: 

[source, sh]
----
user@val:~$ sudo sh -c 'cat > /etc/apache2/sites-available/ykval-ssl.conf'
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.

[source, sh]
----
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.

[source, sh]
----
user@val:~$ sudo sh -c 'cat > /etc/apache2/sites-available/ykval-ssl.conf'
<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.

[source, sh]
----
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:

[source, sh]
----
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:

[source, sh]
----
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):

[source, sh]
----
*.=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:

[source, sh]
----
*.=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:

[source, sh]
----
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:

[source, sh]
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:

[source, sh]
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:

[source, sh]
----
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://developers.yubico.com/yubikey-ksm/Server_Hardening.html on how to
improve security of your system.
