How To Run LinOTP On OpenSuSE 12.3 With PostgreSQL

This tutorial describes the installation of LinOTP on OpenSUSE 12.3 using PostgreSQL as a token database. LinOTP is a two factor authentication solution with One Time Passwords. In the following Howto we are showing how to enable SSH authentication with LinOTP.


Getting prepared

We need to install the following packages:

zypper install python-virtualenv
zypper install gcc
zypper install python-devel
zypper install zlib-devel


Setting up postgresql

LinOTP uses a database to store the token information. You can use any database on another server, you can use an existing database or you can install a database on the very same machine. In this example we are using  PostgreSQL on the same machine.

So we install the necessary packages:

zypper install postgresql postgresql-server
zypper install postgresql-devel

linux-6a8c:/opt/LINOTP # su - postgres
[email protected]:~> initdb -D /var/lib/pgsql/data/

The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".
creating directory /var/lib/pgsql/data ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 24MB
creating configuration files ... ok
creating template1 database in /var/lib/pgsql/data/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating collations ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok
WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.
Success. You can now start the database server using:
postgres -D /var/lib/pgsql/data
pg_ctl -D /var/lib/pgsql/data -l logfile start

[email protected]:~> pg_ctl -D /var/lib/pgsql/data -l pglog.log start

Now, create a postgres user, create the database and grant access to this database:

[email protected]:~> createuser -P linotp2



postgres=# GRANT ALL ON DATABASE LinOTP2 TO linotp2;



Setting up linotp

We are setting up LinOTP using a virtual python environment. This is also described here:

linux-6a8c:~ # mkdir /opt/LINOTP

linux-6a8c:~ # cd /opt/

linux-6a8c:/opt # virtualenv /opt/LINOTP/

linux-6a8c:/opt # cd LINOTP/

linux-6a8c:/opt/LINOTP # source bin/activate


The changed prompt (LINOTP) indicates, that you are now in the python virtual environment.

Now you can install linotp from PyPI. All dependencies are also installed.

(LINOTP)linux-6a8c:/opt/LINOTP # pip install linotp linotpuseridresolver pil configobj psycopg2

Create necessary directories and config file:

(LINOTP)linux-6a8c:/opt/LINOTP # mkdir /var/log/linotp

(LINOTP)linux-6a8c:/opt/LINOTP # cp etc/linotp2/linotp.ini.example etc/linotp2/linotp.ini

Start postgres and make it start at boot time:

(LINOTP)linux-6a8c:/opt/LINOTP # service postgresql start

(LINOTP)linux-6a8c:/opt/LINOTP # chkconfig postgresql on

Create a local postgres user:

useradd -r linotp2

Now you need to edit the linotp.ini configuration file and adapt the following line:

sqlalchemy.url = postgresql+psycopg2://linotp2:[email protected]/linotp2

Please note: PostgreSQL uses small characters for the database name!

Now you need to create the encryption key:

(LINOTP)linux-6a8c:/opt/LINOTP # dd if=/dev/urandom of=etc/linotp2/encKey bs=1 count=96

Then create the database tables:

(LINOTP)linux-6a8c:/opt/LINOTP # paster setup-app etc/linotp2/linotp.ini
Running setup_app() from linotp.websetup

Now you can test your server:

(LINOTP)linux-6a8c:/opt/LINOTP # paster serve etc/linotp2/linotp.ini
Starting server in PID 12355.
serving on view at

Now you can point your browser to http://127.0.01:5001/manage and start to configure your LinOTP-Server, create a new useridresolver, a realm and enroll tokens for the users.

See the how to do the first steps at


Configuring Apache

You might want to run LinOTP within Apache or any other web server, that supports wsgi. Thus you get authentication and encryption.

First we need to install some packages and activate modules:

zypper install apache2 apache2-mod_wsgi
a2enmod wsgi
a2enmod ssl

chkconfig apache2 on
service apache2 start

Open the https port in the firewall and restart the firewall:

SuSEfirewall2 open EXT TCP https
SuSEfirewall2: Setting up rules from /etc/sysconfig/SuSEfirewall2 ...
SuSEfirewall2: Firewall rules successfully set



Create a self signed certificate:

cp etc/apache2/sites-available/linotp2 /etc/apache2/vhosts.d/linotp.conf
openssl genrsa -out /etc/ssl/private/linotpserver.key 2048
chmod 400 /etc/ssl/private/linotpserver.key
openssl req -x509 -new -key /etc/ssl/private/linotpserver.key -out /etc/ssl/certs/linotpserver.pem



Now edit your /etc/apache2/vhosts.d/linotp.conf:

 WSGIPythonHome /opt/LINOTP
<VirtualHost _default_:443>
        ServerAdmin [email protected]

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

        <Directory /opt/LINOTP/etc/linotp2/>
          Order allow,deny
          Allow from all

        WSGIScriptAlias /       /opt/LINOTP/etc/linotp2/linotpapp.wsgi
        # The daemon is running as user 'linotp'
        # This user should have access to the encKey database encryption file
        WSGIDaemonProcess linotp processes=1 threads=15 display-name=%{GROUP} user=linotp
        WSGIProcessGroup linotp
        WSGIPassAuthorization On

        <Location /admin>
                AuthType Digest
                AuthName "LinOTP2 admin area"
                AuthDigestProvider file
                AuthUserFile /opt/LINOTP/etc/linotp2/admins
                Require valid-user

        <Location /audit>
                AuthType Digest
                AuthName "LinOTP2 admin area"
                AuthDigestProvider file
                AuthUserFile /opt/LINOTP/etc/linotp2/admins
                Require valid-user

        <Location /gettoken>
                AuthType Digest
                AuthName "LinOTP2 gettoken"
                AuthDigestProvider file
                AuthUserFile /opt/LINOTP/etc/linotp2/gettoken-api
                Require valuid-user

        <Location /mlogout>
                AuthType Digest
                AuthName "LinOTP2 admin area"
                AuthDigestProvider file
                AuthUserFile /etc/linotp2/admins
                AuthDigestProvider file
                AuthUserFile /etc/linotp2/admins
                Require user EXIT

        <Location /manage>
                AuthType Digest
                AuthName "LinOTP2 admin area"
                AuthDigestProvider file
                AuthUserFile /opt/LINOTP/etc/linotp2/admins
                Require valid-user

        <Location /selfservice>
                # THe authentication for selfservice is done from within the application

        <Location /system>
                AuthType Digest
                AuthName "LinOTP2 admin area"
                AuthDigestProvider file
                AuthUserFile /opt/LINOTP/etc/linotp2/admins
                Require valid-user

        <Location /license>
                # This should have at least the same access rights like /system
               AuthType Digest
               AuthName "LinOTP2 admin area"
               AuthDigestProvider file
               AuthUserFile /opt/LINOTP/etc/linotp2/admins
               Require valid-user

        <Location /validate>
        # No Authentication

        LogLevel info
        ErrorLog /var/log/apache2/error_log

        # Do not use %q! This will reveal all parameters, including setting PINs and Keys!
        # Using SSL_CLINET_S_DN_CN will show you, which administrator did what task
        LogFormat "%h %l %u %t %>s \"%m %U %H\"  %b \"%{Referer}i\" \"%{User-agent}i\" \"%{SSL_CLIENT_S_DN_CN}x\"" LinOTP2
        CustomLog /var/log/apache2/ssl_access.log LinOTP2

        #   SSL Engine Switch:
        #   Enable/Disable SSL for this virtual host.
        SSLEngine on

        #   If both key and certificate are stored in the same file, only the
        #   SSLCertificateFile directive is needed.
        SSLCertificateFile    /etc/apache2/ssl.crt/server.crt
        SSLCertificateKeyFile /etc/apache2/ssl.key/server.key

        <FilesMatch "\.(cgi|shtml|phtml|php)$">
                SSLOptions +StdEnvVars
        <Directory /usr/lib/cgi-bin>
                SSLOptions +StdEnvVars
        BrowserMatch ".*MSIE.*" \
                nokeepalive ssl-unclean-shutdown \
                downgrade-1.0 force-response-1.0

        ErrorDocument 500 "<h1>Internal Server Error</h1> Possible reasons can be missing modules or bad access rights on LinOTP configuration files or log files. Please check the apache logfile <pre>/var/log/apache2/error.log</pre> for more details."


Create your first administrator account:

htdigest2 -c etc/linotp2/admins "LinOTP2 admin area" admin

Create a local account, under which the LinOTP wsgi runs:

useradd -r linotp

Do some Apache stuff:

a2enmod mod_auth_digest

a2dismod php5

a2dismod cgi

a2dismod userdir
a2dismod auth_basic

Edit /etc/sysconfig/apache2 to make Apache start using SSL:


Finally, make the services start at boot time and adapt access rights on files:

chkconfig postgresql on
chkconfig apache2 on
chown -R linotp /var/log/linotp
chown -R linotp /opt/LINOTP/etc/linotp2/data/

Share this page:

0 Comment(s)

Add comment