user@val:~$ sudo apt-get install git make
...
user@val:~$ git clone https://github.com/Yubico/yubikey-val.git
...
user@val:~$ cd yubikey-val
user@val:~/yubikey-val$ sudo make install
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.
Otherwise we recommend the "soft" 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.
The following steps apply 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 14.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.
First you should download and install the latest YK-VAL release:
user@val:~$ sudo apt-get install git make
...
user@val:~$ git clone https://github.com/Yubico/yubikey-val.git
...
user@val:~$ cd yubikey-val
user@val:~/yubikey-val$ sudo make install
Depending on your distribution, the group of Apache (or the HTTP server) might
be different from www-data
, used in Debian and Ubuntu. On Red Hat, Fedora or
CentOS the group is apache
and in SUSE it is www
.
user@val:~/yubikey-val: sudo make install wwwgroup=apache
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.
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.
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.
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,INSERT,UPDATE(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:~$
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,INSERT,UPDATE 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 &
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.
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.
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);
Create an apache web configuration file for the normal HTTP interface like this:
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.
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.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:~$
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.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:~$
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:~$
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.
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
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.
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
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.
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.