# Install Server CE

Installation instruction for the Community Edition of the Psono server

# Preamble

At this point we assume that you already have a postgres database running, ready for connections. If not follow this guide to setup postgres.

# Installation with Docker

  1. Create a settings.yaml in e.g. /opt/docker/psono/ with the following content

    # generate the following six parameters with the following command
    # docker run --rm -ti psono/psono-server:latest python3 ./psono/manage.py generateserverkeys
    SECRET_KEY: 'SOME SUPER SECRET KEY THAT SHOULD BE RANDOM AND 32 OR MORE DIGITS LONG'
    ACTIVATION_LINK_SECRET: 'SOME SUPER SECRET ACTIVATION LINK SECRET THAT SHOULD BE RANDOM AND 32 OR MORE DIGITS LONG'
    DB_SECRET: 'SOME SUPER SECRET DB SECRET THAT SHOULD BE RANDOM AND 32 OR MORE DIGITS LONG'
    EMAIL_SECRET_SALT: '$2b$12$XUG.sKxC2jmkUvWQjg53.e'
    PRIVATE_KEY: '02...0b'
    PUBLIC_KEY: '02...0b'
    
    # The URL of the web client (path to e.g activate.html without the trailing slash)
    # WEB_CLIENT_URL: 'https://psono.example.com'
    
    # Switch DEBUG to false if you go into production
    DEBUG: False
    
    # Adjust this according to Django Documentation https://docs.djangoproject.com/en/2.2/ref/settings/
    ALLOWED_HOSTS: ['*']
    
    # Should be your domain without "www.". Will be the last part of the username
    ALLOWED_DOMAINS: ['example.com']
    
    # If you want to disable registration, you can comment in the following line
    # ALLOW_REGISTRATION: False
    
    # If you want to disable the lost password functionality, you can comment in the following line
    # ALLOW_LOST_PASSWORD: False
    
    # If you want to enforce that the email address and username needs to match upon registration
    # ENFORCE_MATCHING_USERNAME_AND_EMAIL: False
    
    # If you want to restrict registration to some email addresses you can specify here a list of domains to filter
    # REGISTRATION_EMAIL_FILTER: ['company1.com', 'company2.com']
    
    # Should be the URL of the host under which the host is reachable
    # If you open the url and append /info/ to it you should have a text similar to {"info":"{\"version\": \"....}
    HOST_URL: 'https://psono.example.com/server'
    
    # The email used to send emails, e.g. for activation
    # ATTENTION: If executed in a docker container, then "localhost" will resolve to the docker container, so
    # "localhost" will not work as host. Use the public IP or DNS record of the server.
    EMAIL_FROM: 'the-mail-for-for-example-useraccount-activations@test.com'
    EMAIL_HOST: 'smtp.example.com'
    EMAIL_HOST_USER: ''
    EMAIL_HOST_PASSWORD : ''
    EMAIL_PORT: 25
    EMAIL_SUBJECT_PREFIX: ''
    EMAIL_USE_TLS: False
    EMAIL_USE_SSL: False
    EMAIL_SSL_CERTFILE:
    EMAIL_SSL_KEYFILE:
    EMAIL_TIMEOUT: 10
    
    # In case one wants to use mailgun, comment in below lines and provide the mailgun access key and server name
    # EMAIL_BACKEND: 'anymail.backends.mailgun.EmailBackend'
    # MAILGUN_ACCESS_KEY: ''
    # MAILGUN_SERVER_NAME: ''
    
    # In case you want to offer Yubikey support, create a pair of credentials here https://upgrade.yubico.com/getapikey/
    # and update the following two lines before commenting them in
    # YUBIKEY_CLIENT_ID: '123456'
    # YUBIKEY_SECRET_KEY: '8I65IA6ASDFIUHGIH5021FKJA='
    
    # If you have your own Yubico servers, you can specify here the urls as a list
    # YUBICO_API_URLS: ['https://api.yubico.com/wsapi/2.0/verify']
    
    # Cache enabled without belows Redis may lead to unexpected behaviour
    
    # Cache with Redis
    # By default you should use something different than database 0 or 1, e.g. 13 (default max is 16, can be configured in
    # redis.conf) possible URLS are:
    #    redis://[:password]@localhost:6379/0
    #    rediss://[:password]@localhost:6379/0
    #    unix://[:password]@/path/to/socket.sock?db=0
    # CACHE_ENABLE: False
    # CACHE_REDIS: False
    # CACHE_REDIS_LOCATION: 'redis://127.0.0.1:6379/13'
    
    # Enables the management API, required for the psono-admin-client / admin portal (Default is set to False)
    # MANAGEMENT_ENABLED: True
    
    # Enables the fileserver API, required for the psono-fileserver
    # FILESERVER_HANDLER_ENABLED: False
    
    # Enables files for the client
    # FILES_ENABLED: False
    
    # Allows that users can search for partial usernames
    # ALLOW_USER_SEARCH_BY_USERNAME_PARTIAL: True
    
    # Allows that users can search for email addresses too
    # ALLOW_USER_SEARCH_BY_EMAIL: True
    
    # Disables central security reports
    # DISABLE_CENTRAL_SECURITY_REPORTS: True
    
    # Configures a system wide DUO connection for all clients
    # DUO_INTEGRATION_KEY: ''
    # DUO_SECRET_KEY: ''
    # DUO_API_HOSTNAME: ''
    
    # If you are using the DUO proxy, you can configure here the necessary HTTP proxy
    # DUO_PROXY_HOST: 'the-ip-or-dns-name-goes-here'
    # DUO_PROXY_PORT: 80
    # DUO_PROXY_TYPE: 'CONNECT'
    # If your proxy requires specific headers you can also configure these here
    # DUO_PROXY_HEADERS: ''
    
    # Normally only one of the configured second factors needs to be solved. Setting this to True forces the client to solve all
    # MULTIFACTOR_ENABLED: True
    
    # Allows admins to limit the offered second factors in the client
    # ALLOWED_SECOND_FACTORS: ['yubikey_otp', 'google_authenticator', 'duo', 'webauthn']
    
    # Your Postgres Database credentials
    # ATTENTION: If executed in a docker container, then "localhost" will resolve to the docker container, so
    # "localhost" will not work as host. Use the public IP or DNS record of the server.
    DATABASES:
        default:
            'ENGINE': 'django.db.backends.postgresql_psycopg2'
            'NAME': 'psono'
            'USER': 'psono'
            'PASSWORD': 'password'
            'HOST': 'localhost'
            'PORT': '5432'
    # for master / slave replication setup comment in the following (all reads will be redirected to the slave
    #    slave:
    #        'ENGINE': 'django.db.backends.postgresql_psycopg2'
    #        'NAME': 'YourPostgresDatabase'
    #        'USER': 'YourPostgresUser'
    #        'PASSWORD': 'YourPostgresPassword'
    #        'HOST': 'YourPostgresHost'
    #        'PORT': 'YourPostgresPort'
    
    # The path to the template folder can be "shadowed" if required later
    TEMPLATES: [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': ['/root/psono/templates'],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    Update database credentials / secrets / paths like described in the comments

  2. Test E-Mail

    The most tedious step is usually for me to get e-mail working. To make this step easier, we offer a small test script which will send a test e-mail.

    To send a test e-mail to something@something.com execute:

    docker run --rm \
      -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
      -ti psono/psono-server:latest python3 ./psono/manage.py sendtestmail something@something.com
    

    If you receive this test e-mail, it should be configured properly.

  3. Prepare the database

    docker run --rm \
      -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
      -ti psono/psono-server:latest python3 ./psono/manage.py migrate
    
  4. Run the Psono server container and expose the server port

    docker run --name psono-server \
        --sysctl net.core.somaxconn=65535 \
        -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
        -d --restart=unless-stopped -p 10100:80 psono/psono-server:latest
    

    This will start the Psono server on port 10100. If you open now http://your-ip:10100/info/ you should see something like this:

    {"info":"{\"version\": \"....}
    

    If you don't, please make sure no firewall is blocking your request.

  5. Setup cleanup job

    Execute the following command:

    crontab -e
    

    and add the following line:

    30 2 * * * docker run --rm -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml -ti psono/psono-server:latest python3 ./psono/manage.py cleartoken >> /var/log/cron.log 2>&1
    
  6. Setup Reverse Proxy

    To run the Psono password manager in production, a reverse proxy is needed, to handle the ssl offloading and glue the Psono server and webclient together. Follow the guide to setup reverse proxy as a next step.

# Installation for Ubuntu

This guide will install the Psono server, and runs it with gunicorn and nginx. It has been tested on Ubuntu 18.04.

WARNING

This installation type is meant for developers and auditors. For production purposes we recommend the docker installation.

  1. Become root

    sudo -i
    
  2. Install some generic stuff

    apt-get update
    apt-get install -y \
            git \
            libyaml-dev \
            libpython3-dev \
            libpq-dev \
            libffi-dev \
            python3-dev \
            python-pip \
            python3-pip \
            python3-psycopg2 \
            postgresql-client \
            haveged \
            libsasl2-dev \
            libldap2-dev \
            libssl-dev \
            supervisor
    pip3 install gunicorn
    
  3. Create psono user

    adduser psono
    
  4. Become the psono user

    su psono
    
  5. Clone git repository

    git clone https://gitlab.com/psono/psono-server.git ~/psono-server
    
  6. Install python requirements

    Ctrl + D                            # become root again
    cd /home/psono/psono-server
    pip3 install -r requirements.txt
    su psono                            # become psono again
    
  7. Create settings folder

    mkdir ~/.psono_server
    
  8. Create a settings.yaml in ~/.psono_server/ with the following content

    # generate the following six parameters with the following command
    # python3 ~/psono-server/psono/manage.py generateserverkeys
    SECRET_KEY: 'Ndhz7mBYUVDEG9hNeYVgPcE4a4MRJiLQADQ5DNOUV2l7OmyAFtQ6hR6GyIErr7xF'
    ACTIVATION_LINK_SECRET: 'TT3dXwD6lysyqthlpQUl8WlGPeG6WSVdQBd0JBOS9ZwNynuuPsHfPJ7ComLVQsyE'
    DB_SECRET: 'TXUXjPOvsWYGnBNnKrITd1tvWvKOmk1sN8FMJopDuCtPT1y2pbP56R9pWIEHjAJV'
    EMAIL_SECRET_SALT: '$2b$12$o9HKh8yvEqYe6k0Do/YZdu'
    PRIVATE_KEY: 'debe5115baf449b0c53ac112d5df314bb7adcb46308b6847908c97b06746a65c'
    PUBLIC_KEY: '9b8f35c6261fa9840f00b86026fe11ec4e0e0cbf84b2f860713da1e442c79577'
    
    # The URL of the web client (path to e.g activate.html without the trailing slash)
    # WEB_CLIENT_URL: 'https://psono.example.com'
    
    # Switch DEBUG to false if you go into production
    DEBUG: False
    
    # Adjust this according to Django Documentation https://docs.djangoproject.com/en/2.2/ref/settings/
    ALLOWED_HOSTS: ['*']
    
    # Should be your domain without "www.". Will be the last part of the username
    ALLOWED_DOMAINS: ['example.com']
    
    # If you want to disable registration, you can comment in the following line
    # ALLOW_REGISTRATION: False
    
    # If you want to disable the lost password functionality, you can comment in the following line
    # ALLOW_LOST_PASSWORD: False
    
    # If you want to enforce that the email address and username needs to match upon registration
    # ENFORCE_MATCHING_USERNAME_AND_EMAIL: False
    
    # If you want to restrict registration to some email addresses you can specify here a list of domains to filter
    # REGISTRATION_EMAIL_FILTER: ['company1.com', 'company2.com']
    
    # Should be the URL of the host under which the host is reachable
    # If you open the url and append /info/ to it you should have a text similar to {"info":"{\"version\": \"....}
    HOST_URL: 'https://psono.example.com/server'
    
    # The email used to send emails, e.g. for activation
    # ATTENTION: If executed in a docker container, then "localhost" will resolve to the docker container, so
    # "localhost" will not work as host. Use the public IP or DNS record of the server.
    EMAIL_FROM: 'the-mail-for-for-example-useraccount-activations@test.com'
    EMAIL_HOST: 'smtp.example.com'
    EMAIL_HOST_USER: ''
    EMAIL_HOST_PASSWORD : ''
    EMAIL_PORT: 25
    EMAIL_SUBJECT_PREFIX: ''
    EMAIL_USE_TLS: False
    EMAIL_USE_SSL: False
    EMAIL_SSL_CERTFILE:
    EMAIL_SSL_KEYFILE:
    EMAIL_TIMEOUT: 10
    
    # In case one wants to use mailgun, comment in below lines and provide the mailgun access key and server name
    # EMAIL_BACKEND: 'anymail.backends.mailgun.EmailBackend'
    # MAILGUN_ACCESS_KEY: ''
    # MAILGUN_SERVER_NAME: ''
    
    # In case you want to offer Yubikey support, create a pair of credentials here https://upgrade.yubico.com/getapikey/
    # and update the following two lines before commenting them in
    # YUBIKEY_CLIENT_ID: '123456'
    # YUBIKEY_SECRET_KEY: '8I65IA6ASDFIUHGIH5021FKJA='
    
    # If you have your own Yubico servers, you can specify here the urls as a list
    # YUBICO_API_URLS: ['https://api.yubico.com/wsapi/2.0/verify']
    
    # Cache enabled without belows Redis may lead to unexpected behaviour
    
    # Cache with Redis
    # By default you should use something different than database 0 or 1, e.g. 13 (default max is 16, can be configured in
    # redis.conf) possible URLS are:
    #    redis://[:password]@localhost:6379/0
    #    rediss://[:password]@localhost:6379/0
    #    unix://[:password]@/path/to/socket.sock?db=0
    # CACHE_ENABLE: False
    # CACHE_REDIS: False
    # CACHE_REDIS_LOCATION: 'redis://127.0.0.1:6379/13'
    
    # Enables the management API, required for the psono-admin-client / admin portal
    # MANAGEMENT_ENABLED: False
    
    # Enables the fileserver API, required for the psono-fileserver
    # FILESERVER_HANDLER_ENABLED: False
    
    # Enables files for the client
    # FILES_ENABLED: False
    
    # Allows that users can search for partial usernames
    # ALLOW_USER_SEARCH_BY_USERNAME_PARTIAL: True
    
    # Allows that users can search for email addresses too
    # ALLOW_USER_SEARCH_BY_EMAIL: True
    
    # Disables central security reports
    # DISABLE_CENTRAL_SECURITY_REPORTS: True
    
    # Configures a system wide DUO connection for all clients
    # DUO_INTEGRATION_KEY: ''
    # DUO_SECRET_KEY: ''
    # DUO_API_HOSTNAME: ''
    
    # If you are using the DUO proxy, you can configure here the necessary HTTP proxy
    # DUO_PROXY_HOST: 'the-ip-or-dns-name-goes-here'
    # DUO_PROXY_PORT: 80
    # DUO_PROXY_TYPE: 'CONNECT'
    # If your proxy requires specific headers you can also configure these here
    # DUO_PROXY_HEADERS: ''
    
    # Normally only one of the configured second factors needs to be solved. Setting this to True forces the client to solve all
    # MULTIFACTOR_ENABLED: True
    
    # Allows admins to limit the offered second factors in the client
    # ALLOWED_SECOND_FACTORS: ['yubikey_otp', 'google_authenticator', 'duo', 'webauthn']
    
    # Your Postgres Database credentials
    # ATTENTION: If executed in a docker container, then "localhost" will resolve to the docker container, so
    # "localhost" will not work as host. Use the public IP or DNS record of the server.
    DATABASES:
        default:
            'ENGINE': 'django.db.backends.postgresql_psycopg2'
            'NAME': 'psono'
            'USER': 'psono'
            'PASSWORD': 'password'
            'HOST': 'localhost'
            'PORT': '5432'
    # for master / slave replication setup comment in the following (all reads will be redirected to the slave
    #    slave:
    #        'ENGINE': 'django.db.backends.postgresql_psycopg2'
    #        'NAME': 'YourPostgresDatabase'
    #        'USER': 'YourPostgresUser'
    #        'PASSWORD': 'YourPostgresPassword'
    #        'HOST': 'YourPostgresHost'
    #        'PORT': 'YourPostgresPort'
    
    # Update the path to your templates folder
    # If you do not want to change it (yet) you can leave it like it is.
    TEMPLATES: [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': ['/home/psono/psono-server/psono/templates'],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    Update database credentials / secrets / paths like described in the comments

  9. Test E-Mail

    The most tedious step is usually for me to get e-mail working. To make this step easier, we offer a small test script which will send a test e-mail.

    To send a test e-mail to something@something.com execute:

    python3 ~/psono-server/psono/manage.py sendtestmail something@something.com
    

    If you receive this test e-mail, it should be configured properly.

  10. Create our database

    python3  ~/psono-server/psono/manage.py migrate
    
  11. Run the Psono server

    cd ~/psono-server/psono
    gunicorn --bind 0.0.0.0:10100 wsgi
    

    This will start the Psono server on port 10100. If you open now http://your-ip:10100/info/ should see something like this:

    {"info":"{\"version\": \"....}
    

    If you don't, please make sure no firewall is blocking your request.

  12. Become root again

    Ctrl + D
    
  13. Create supervisor config

    Create a psono-server.conf in /etc/supervisor/conf.d/ with the following content:

    [program:psono-server]
    command = /usr/local/bin/gunicorn --bind 127.0.0.1:10100 wsgi
    directory=/home/psono/psono-server/psono
    user = psono
    autostart=true
    autorestart=true
    redirect_stderr=true
    

    You may have realised that we changed the bind. This way Psono is only accessible from localhost, which is fine as we will proxy requests with nginx.

  14. Reload supervisorctl

    supervisorctl reload
    

    Now you can control the Psono server with supervisorctrl commands e.g.

    • supervisorctl status psono-server
    • supervisorctl start psono-server
    • supervisorctl stop psono-server
  15. Setup cleanup job

    Execute the following command:

    crontab -e
    

    and add the following line:

    30 2 * * * psono python3 /home/psono/psono-server/psono/manage.py cleartoken >> /var/log/cron.log 2>&1
    
  16. Setup Reverse Proxy

    To run the Psono password manager in production, a reverse proxy is needed, to handle the ssl offloading and glue the Psono server and webclient together. Follow the guide to setup reverse proxy as a next step.

# Installation for CentOS

This guide will install the Psono server, and runs it with gunicorn and nginx. It has been tested on CentOS 7.

WARNING

This installation type is meant for developers and auditors. For production purposes we recommend the docker installation.

  1. Become root

    sudo -i
    
  2. Install some generic stuff

    yum -y update
    yum -y install https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    yum -y update
    yum -y install \
                git \
                gcc \
                curl \
                openssl-devel \
                bzip2-devel \
                libffi-devel \
                postgresql \
                postgresql-devel \
                haveged \
                openldap-devel \
                make \
                supervisor
    curl -O https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz
    tar -xzf Python-3.8.1.tgz
    cd Python-3.8.1/
    ./configure --enable-optimizations
    make altinstall
    ln -s /usr/local/bin/python3.8 /usr/bin/python3
    ln -s /usr/local/bin/pip3.8 /usr/bin/pip3
    pip3 install gunicorn
    
  3. Create psono user

    adduser psono
    
  4. Become the psono user

    su psono
    
  5. Clone git repository

    git clone https://gitlab.com/psono/psono-server.git ~/psono-server
    
  6. Install python requirements

    Ctrl + D                            # become root again
    cd /home/psono/psono-server
    pip3 install -r requirements.txt
    su psono                            # become psono again
    
  7. Create settings folder

    mkdir ~/.psono_server
    
  8. Create a settings.yaml in ~/.psono_server/ with the following content

    # generate the following six parameters with the following command
    # python3 ~/psono-server/psono/manage.py generateserverkeys
    SECRET_KEY: 'Ndhz7mBYUVDEG9hNeYVgPcE4a4MRJiLQADQ5DNOUV2l7OmyAFtQ6hR6GyIErr7xF'
    ACTIVATION_LINK_SECRET: 'TT3dXwD6lysyqthlpQUl8WlGPeG6WSVdQBd0JBOS9ZwNynuuPsHfPJ7ComLVQsyE'
    DB_SECRET: 'TXUXjPOvsWYGnBNnKrITd1tvWvKOmk1sN8FMJopDuCtPT1y2pbP56R9pWIEHjAJV'
    EMAIL_SECRET_SALT: '$2b$12$o9HKh8yvEqYe6k0Do/YZdu'
    PRIVATE_KEY: 'debe5115baf449b0c53ac112d5df314bb7adcb46308b6847908c97b06746a65c'
    PUBLIC_KEY: '9b8f35c6261fa9840f00b86026fe11ec4e0e0cbf84b2f860713da1e442c79577'
    
    # The URL of the web client (path to e.g activate.html without the trailing slash)
    # WEB_CLIENT_URL: 'https://psono.example.com'
    
    # Switch DEBUG to false if you go into production
    DEBUG: False
    
    # Adjust this according to Django Documentation https://docs.djangoproject.com/en/2.2/ref/settings/
    ALLOWED_HOSTS: ['*']
    
    # Should be your domain without "www.". Will be the last part of the username
    ALLOWED_DOMAINS: ['example.com']
    
    # If you want to disable registration, you can comment in the following line
    # ALLOW_REGISTRATION: False
    
    # If you want to disable the lost password functionality, you can comment in the following line
    # ALLOW_LOST_PASSWORD: False
    
    # If you want to enforce that the email address and username needs to match upon registration
    # ENFORCE_MATCHING_USERNAME_AND_EMAIL: False
    
    # If you want to restrict registration to some email addresses you can specify here a list of domains to filter
    # REGISTRATION_EMAIL_FILTER: ['company1.com', 'company2.com']
    
    # Should be the URL of the host under which the host is reachable
    # If you open the url and append /info/ to it you should have a text similar to {"info":"{\"version\": \"....}
    HOST_URL: 'https://psono.example.com/server'
    
    # The email used to send emails, e.g. for activation
    # ATTENTION: If executed in a docker container, then "localhost" will resolve to the docker container, so
    # "localhost" will not work as host. Use the public IP or DNS record of the server.
    EMAIL_FROM: 'the-mail-for-for-example-useraccount-activations@test.com'
    EMAIL_HOST: 'smtp.example.com'
    EMAIL_HOST_USER: ''
    EMAIL_HOST_PASSWORD : ''
    EMAIL_PORT: 25
    EMAIL_SUBJECT_PREFIX: ''
    EMAIL_USE_TLS: False
    EMAIL_USE_SSL: False
    EMAIL_SSL_CERTFILE:
    EMAIL_SSL_KEYFILE:
    EMAIL_TIMEOUT: 10
    
    # In case one wants to use mailgun, comment in below lines and provide the mailgun access key and server name
    # EMAIL_BACKEND: 'anymail.backends.mailgun.EmailBackend'
    # MAILGUN_ACCESS_KEY: ''
    # MAILGUN_SERVER_NAME: ''
    
    # In case you want to offer Yubikey support, create a pair of credentials here https://upgrade.yubico.com/getapikey/
    # and update the following two lines before commenting them in
    # YUBIKEY_CLIENT_ID: '123456'
    # YUBIKEY_SECRET_KEY: '8I65IA6ASDFIUHGIH5021FKJA='
    
    # If you have your own Yubico servers, you can specify here the urls as a list
    # YUBICO_API_URLS: ['https://api.yubico.com/wsapi/2.0/verify']
    
    # Cache enabled without belows Redis may lead to unexpected behaviour
    
    # Cache with Redis
    # By default you should use something different than database 0 or 1, e.g. 13 (default max is 16, can be configured in
    # redis.conf) possible URLS are:
    #    redis://[:password]@localhost:6379/0
    #    rediss://[:password]@localhost:6379/0
    #    unix://[:password]@/path/to/socket.sock?db=0
    # CACHE_ENABLE: False
    # CACHE_REDIS: False
    # CACHE_REDIS_LOCATION: 'redis://127.0.0.1:6379/13'
    
    # Enables the management API, required for the psono-admin-client / admin portal
    # MANAGEMENT_ENABLED: False
    
    # Enables the fileserver API, required for the psono-fileserver
    # FILESERVER_HANDLER_ENABLED: False
    
    # Enables files for the client
    # FILES_ENABLED: False
    
    # Allows that users can search for partial usernames
    # ALLOW_USER_SEARCH_BY_USERNAME_PARTIAL: True
    
    # Allows that users can search for email addresses too
    # ALLOW_USER_SEARCH_BY_EMAIL: True
    
    # Disables central security reports
    # DISABLE_CENTRAL_SECURITY_REPORTS: True
    
    # Configures a system wide DUO connection for all clients
    # DUO_INTEGRATION_KEY: ''
    # DUO_SECRET_KEY: ''
    # DUO_API_HOSTNAME: ''
    
    # If you are using the DUO proxy, you can configure here the necessary HTTP proxy
    # DUO_PROXY_HOST: 'the-ip-or-dns-name-goes-here'
    # DUO_PROXY_PORT: 80
    # DUO_PROXY_TYPE: 'CONNECT'
    # If your proxy requires specific headers you can also configure these here
    # DUO_PROXY_HEADERS: ''
    
    # Normally only one of the configured second factors needs to be solved. Setting this to True forces the client to solve all
    # MULTIFACTOR_ENABLED: True
    
    # Allows admins to limit the offered second factors in the client
    # ALLOWED_SECOND_FACTORS: ['yubikey_otp', 'google_authenticator', 'duo', 'webauthn']
    
    # Your Postgres Database credentials
    # ATTENTION: If executed in a docker container, then "localhost" will resolve to the docker container, so
    # "localhost" will not work as host. Use the public IP or DNS record of the server.
    DATABASES:
        default:
            'ENGINE': 'django.db.backends.postgresql_psycopg2'
            'NAME': 'psono'
            'USER': 'psono'
            'PASSWORD': 'password'
            'HOST': 'localhost'
            'PORT': '5432'
    # for master / slave replication setup comment in the following (all reads will be redirected to the slave
    #    slave:
    #        'ENGINE': 'django.db.backends.postgresql_psycopg2'
    #        'NAME': 'YourPostgresDatabase'
    #        'USER': 'YourPostgresUser'
    #        'PASSWORD': 'YourPostgresPassword'
    #        'HOST': 'YourPostgresHost'
    #        'PORT': 'YourPostgresPort'
    
    # Update the path to your templates folder
    # If you do not want to change it (yet) you can leave it like it is.
    TEMPLATES: [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': ['/home/psono/psono-server/psono/templates'],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    Update database credentials / secrets / paths like described in the comments

  9. Test E-Mail

    The most tedious step is usually for me to get e-mail working. To make this step easier, we offer a small test script which will send a test e-mail.

    To send a test e-mail to something@something.com execute:

    python3 ~/psono-server/psono/manage.py sendtestmail something@something.com
    

    If you receive this test e-mail, it should be configured properly.

  10. Create our database

    python3  ~/psono-server/psono/manage.py migrate
    
  11. Run the Psono server

    cd ~/psono-server/psono
    gunicorn --bind 0.0.0.0:10100 wsgi
    

    This will start the Psono server on port 10100. If you open now http://your-ip:10100/info/ you should see something like this:

    {"info":"{\"version\": \"....}
    

    If you don't, please make sure no firewall is blocking your request.

  12. Become root again

    Ctrl + D
    
  13. Create supervisor config

    Create a psono-server.ini in /etc/supervisord.d/ with the following content:

    [program:psono-server]
    command = /usr/bin/gunicorn --bind 0.0.0.0:10100 wsgi
    directory=/home/psono/psono-server/psono
    user = psono
    autostart=true
    autorestart=true
    redirect_stderr=true
    

    You may have realised that we changed the bind. This way Psono is only accessible from localhost, which is fine as we will proxy requests with nginx.

  14. Start supervisord and register autostart

    systemctl enable --now supervisord
    

    Now you can control the Psono server with supervisorctrl commands e.g.

    • supervisorctl status psono-server
    • supervisorctl start psono-server
    • supervisorctl stop psono-server
  15. Setup cleanup job

    Execute the following command:

    crontab -e
    

    and add the following line:

    30 2 * * * psono python3 /home/psono/psono-server/psono/manage.py cleartoken >> /var/log/cron.log 2>&1
    
  16. Setup Reverse Proxy

    To run the Psono password manager in production, a reverse proxy is needed, to handle the ssl offloading and glue the Psono server and webclient together. Follow the guide to setup reverse proxy as a next step.

# Note: Installation behind Firewall

If you have put your installation behind a firewall, you have to whitelist some ports / adjust some settings, that all features work:

  • Incoming TCP connections (usually Port 443) from clients to servers
  • Incoming TCP connections (usually Port 443) from fileservers to servers
  • Incoming TCP connections (usually Port 80) from clients to servers (will do the redirect to https)
  • Outgoing TCP 5432 to database server
  • Outgoing TCP / UDP 123 connection to time.google.com: Psono requires a synced time for various reasons (Google Authenticator, YubiKey, Throttling, Replay protection, ...) Therefore it has a healthcheck, to compare the local time to a time server (by default time.google.com). You can specify your own timeserver in the settings.yaml with the "TIME_SERVER" parameter. If you are confident that your server always has the correct time you can also disable the healthcheck with "HEALTHCHECK_TIME_SYNC_ENABLED: False".
  • Outgoing TCP 443 connection to api*.yubico.com: Psono validates YubiKey OTP tokens against the official Yubikey Servers. You can use your own YubiKey server with the "YUBICO_API_URLS" parameter in the settings.yaml
  • Outgoing TCP 443 connection to api*.duosecurity.com: Psono initiates connection to the Duo api server, whenever someone uses Duo's 2 Factor authentication. Psono also supports Duo's auth proxy duo.com/docs/authproxy-reference (opens new window) by configuring DUO_PROXY_HOST, DUO_PROXY_PORT and DUO_PROXY_TYPE
  • Outgoing TCP connection (usually 25, 465, 587 or 2525) to your email server