Psono Passwortmanager install guide

Install the free and opensource password manager Psono

This documentation contains the installation of the Psono password manager in a step-by-step way without the Psono fileserver. Sadly the offical documentation has some lacks in details, so I wrote this guide to make it a bit easier for beginners by adding this missing details. In general the installation of the Community Edition (CE) and the Enterprise Edition (EE) is the same. Only the setting-file is different, which is mentioned in this documentation.

Psono required 2 servers. For small installations with 100 users or less you require

  • A database server with 1 CPU, 1.5GB memory and 20GB disk space.
  • An application server with 1 CPU, 1GB memory and 10GB disk space. For setups with more than 100 users, you can find the requirements here: Installation Preparation | Psono Documentation.

Setup Datenbankserver

Prerequirements

  • Debina or Ubuntu server with the latest version and all updates. CentOS is also possible, but not mentioned in this documentation.
  • An internet connection during the installation.

Setup

The database server utilizes a PostreSQL database with version 13 or above. To set it up for Psono, run the follwowing commands:

sudo apt -y install vim bash-completion wget gnupg lsb-release
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" | sudo tee  /etc/apt/sources.list.d/pgdg.list
sudo apt -y update
sudo apt -y install postgresql-13 postgresql-client-13

Switch to the postgres user

sudo -iu postgres

Create the new DB

createdb psono

Open the postgres command prompt

psql psono

Create the psono datebase user and grant the privileges. Replace the password in the command with a secure secret password you created by yourself.

CREATE USER psono WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE "psono" to psono;

Install the required postgres extensions.

CREATE EXTENSION IF NOT EXISTS ltree;
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

Exit the postgres shell

\q (or Ctrl+D)

Exit the postgres user command prompt and go back to your original one

exit

Now we need to configure the pg_hba.conf. This file contains the hosts that are allowed to access which database with which user. By default it is not allowed to connect for external systems to the postgres databases running on a server. So we need to configure it. This is where the official documentation has a lack of information. Open the file with the following command:

sudo nano /etc/postgresql/13/main/pg_hba.conf

At the end of the file add the following lines and replace <IP> with the IP of the Psono application server:

host    psono     psono     127.0.0.1/32      md5
host    psono     psono     ::1/128           md5
host    psono     psono     <IP>/32           md5

Now we also need to allow remoteconnections in general run this command to open the postgresql.conf file:

sudo nano /etc/postgresql/13/main/postgresql.conf

Press Ctrl + W to search for the word listen. The first or second result should be a line, that is not commented out (starts with a #). Replace the line with the following: listen_addresses = '*'

Finally restart the database service and the configuration is done:

sudo systemctl restart postresql

Install Psono application server

Voraussetzungen

  • Ein Debian oder Ubuntu Server in der aktuellsten Version inkl. aller Updates. Die Installation unter CentOS ist ebenfalls möglich. Darauf gehe ich hier aber nicht ein.
  • Eine Internetverbindung des Servers während der Installation.

Installation

Install docker:

sudo apt install docker.io -y

Run the following command and copy the output to a notepad (or similar) on your local system. This will be required in the next step when creating the settings.yaml.

docker run --rm -ti psono/psono-combo:latest python3 ./psono/manage.py generateserverkeys

Create and open a new settings.yaml file at /opt/docker/psono:

sudo mkdir /opt/docker
sudo mkdir /opt/docker/psono
sudo nano /opt/docker/psono/settings.yaml

Settings file Psono Community Edition (CE)

This is referencing to Install Psono’s combo CE image | Psono Documentation Paste the following content to the file for Psono Community Edition (CE) (for Enterprise Edition see after that block and skip this):

# generate the following six parameters with the following command
# docker run --rm -ti psono/psono-combo: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',
            ],
        },
    },
]

Change the following things in this setting file:

  • Replace the first 6 lines with the secrets and keys with the values you created before.
  • Set ALLOWED_DOMAINS to your domain. This can be an internal domain for internal systems or the external public domain in case your system is reachable from the internet.
  • Set HOST_URL to the URL that will be used to access the server. The “/server” suffix must stay at the end of the URL.
  • The next thing is to configure mail settings, which may be the most challenging thing on the configuration.
    • Set EMAIL_FROM to the mail-address that will be shown as the sender.
    • Set EMAIL_HOST to the SMTP-Servers address
    • Set EMAIL_HOST_USER to the user, that is used to authenticate at the SMTP-Server.
    • Set EMAIL_HOST_PASSWORD to the users password
    • Set EMAIL_PORT to the port used. 25 is the unencrypted SMTP port. 587 is default for StartTLS, 465 is default for SSL encrypted connections.
    • Set EMAIL_SUBJECT_PREFIX to a prefix, if you like, that will be shown in front of all mail titles send by Psonso.
    • Set EMAIL_USE_TLS to True, if you use StartTLS encrypted SMTP connections. Otherwise set to False.
    • Set EMAIL_USE_SSL to True, if you use SSL encrypted SMTP connections. Otherwise set to False.
    • If you have SSL certificates for the SSL encrypted connections, set the values to a local path (i.e. /opt/docker/psono/cert.pem and /opt/docker/psono/cert.key) where you store the certificate and key for the settings EMAIL_SSL_CERTFILE and EMAIL_SSL_KEYFILE.
    • Leave EMAIL_TIMEOUT at the default value of 10 (seconds). If you have a really slow internet connections or other plausible reason to set another value, feel free to do so.
  • At the DATABASES settings part set the PASSWORD to the password you configured at the setup of the database server for the psono-user.
  • Also at DATAGBASES set the HOST value to the IP of the Database Server.

Press Ctrl+O to write the changes and Ctrl+X to exit nano.

Settings.yaml for Psono Enterprise Edition (EE)

This is referencing to Install Psono’s combo EE | Psono Documentation. Paste the following content to the file for Psono Enterprise Edition (EE) (skip this, if you want to install the Community Edition):

# generate the following six parameters with the following command
# docker run --rm -ti psono/psono-combo: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'

# The server will automatically connect to the license server to get a license for 10 users.
# For paying customers we offer the opportunity to get an offline license code.
#
# LICENSE_CODE: |
#   0abcdefg...
#   1abcdefg...
#   2abcdefg...
#   3abcdefg...
#   4abcdefg...
#   5abcdefg...
#   6abcdefg...
#   7abcdefg...
#   8abcdefg...

# 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']

# If you want to use LDAP, then you can configure it like this
#
# 		LDAP_URL: Any valid LDAP string, preferable with ldaps. usual urls are 'ldaps://example.com:636' or 'ldap://192.168.0.1:389'
#		LDAP_DOMAIN: Your LDAP domain, is added at the end of the username to form the full username
#		LDAP_BIND_DN: One User that can be used to search your LDAP
#		LDAP_BIND_PASS: The password of the user specified in LDAP_BIND_DN
#		LDAP_ATTR_GUID: The uuid attribute. e.g. on Windows 'objectGUID', but common are 'GUID' or 'entryUUID', default 'objectGUID'
#		LDAP_OBJECT_CLASS_USER: The objectClass value to filter user objects e.g. on Windows 'user', default 'user'
#		LDAP_OBJECT_CLASS_GROUP: The objectClass value to filter group objects e.g. on Windows 'group', default 'group'
#		LDAP_SEARCH_USER_DN: The "root" from which downwards we search for the users
#		LDAP_SEARCH_GROUP_DN: The "root" from which downwards we search for the groups
#		LDAP_ATTR_USERNAME: The username attribute to try to match against. e.g. on Windows 'sAMAccountName', default 'sAMAccountName'
#		LDAP_ATTR_EMAIL: The attribute of the user objects that holds the mail address e.g. on Windows 'mail', default 'mail'
#		LDAP_ATTR_GROUPS: The attribute of the user objects that holds the groups e.g. on Windows 'memberOf', default 'memberOf'
#		LDAP_REQUIRED_GROUP : The attribute to restrict access / usage. Only members of these groups can connect e.g. ['CN=groupname,OU=something,DC=example,DC=com'], default []
#		LDAP_CA_CERT_FILE: If you want to use ldaps and don't have a publicly trusted and signed certificate you can specify here the path to your ca certificate
#
#		LDAP_MEMBER_OF_OVERLAY: If your server has not this memberOf overlay, you can switch modes with this flag.
#                               Users will be mapped (based on their LDAP_ATTR_GROUP_MEMBER_ATTRIBUTE attribute) to groups (based on their LDAP_ATTR_MEMBERS attribute), default True
#		LDAP_ATTR_GROUP_MEMBER_ATTRIBUTE: The user attribute that will be used to map the group memberships, default 'uid'
#		LDAP_ATTR_MEMBERS: The group attribute that will be used to map the to the users LDAP_ATTR_GROUP_MEMBER_ATTRIBUTE attribute, default 'memberUid'
#
# To help you setup LDAP, we have created a small "testldap" command that should make things a lot easier. You can execute it like:
# docker run --rm \
#  -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
#  -ti psono/psono-combo-enterprise:latest python3 psono/manage.py testldap username@something.com thePassWord
#
# For Windows AD it could look like this:
#
# LDAP : [
#     {
#         'LDAP_URL': 'ldaps://192.168.0.1:636',
#         'LDAP_DOMAIN': 'example.com',
#         'LDAP_BIND_DN': 'CN=LDAPPsono,OU=UsersTech,OU=example.com,DC=example,DC=com',
#         'LDAP_BIND_PASS': 'hopefully_not_123456',
#         'LDAP_SEARCH_USER_DN': 'OU=Users,OU=example.com,DC=example,DC=com',
#         'LDAP_SEARCH_GROUP_DN': 'OU=Groups,OU=example.com,DC=example,DC=com',
#     },
# ]
#
# If your server does not have the memberOf overlay, then you can also do something like this
#
# LDAP : [
#     {
#         'LDAP_URL': 'ldaps://192.168.0.1:636',
#         'LDAP_DOMAIN': 'example.com',
#         'LDAP_BIND_DN': 'CN=LDAPPsono,OU=UsersTech,OU=example.com,DC=example,DC=com',
#         'LDAP_BIND_PASS': 'hopefully_not_123456',
#         'LDAP_SEARCH_USER_DN': 'OU=Users,OU=example.com,DC=example,DC=com',
#         'LDAP_SEARCH_GROUP_DN': 'OU=Groups,OU=example.com,DC=example,DC=com',
#         'LDAP_OBJECT_CLASS_USER': 'posixAccount',
#         'LDAP_OBJECT_CLASS_GROUP': 'posixGroup',
#         'LDAP_ATTR_USERNAME': 'uid',
#         'LDAP_ATTR_GUID': 'entryUUID',
#         'LDAP_MEMBER_OF_OVERLAY': False,
#         'LDAP_ATTR_GROUP_MEMBER_ATTRIBUTE': 'uid',
#         'LDAP_ATTR_MEMBERS': 'memberUid',
#     },
# ]
#
# ATTENTION: API kays currently bypass LDAP authentication, that means API keys can still access secrets even if the
# user was disabled in LDAP. API keys can be disabled with COMPLIANCE_DISABLE_API_KEYS

# You also have to comment in the line below if you want to use LDAP (default: ['AUTHKEY'])
# For SAML authentication, you also have to add 'SAML' to the array.
# AUTHENTICATION_METHODS: ['AUTHKEY', 'LDAP']

# Enable Audit logging
# LOGGING_AUDIT: True

# To log to another destination you can specify this here, default '/var/log/psono'
# Never really necessary, as we will run the Psono server in a docker container and can mount /var/log/psono to any
# location on the underlying docker host.
# LOGGING_AUDIT_FOLDER: '/var/log/psono'

# If you prefer server time over utc, you can do that like below (default 'time_utc')
# LOGGING_AUDIT_TIME: 'time_server'

# If the server logs too much for you can either whitelist or blacklist events by their event code. (default: [])
# LOGGING_AUDIT_WHITELIST: []
# LOGGING_AUDIT_BLACKLIST: []

# If you are having Splunk and don't have a Splunk forwarder that can ship the logs, you can use Psono's native Splunk
# implementation to ship the logs for you. In order for that to work you need a Splunk HTTP EVent Collector to be
# configured as explained here https://dev.splunk.com/enterprise/docs/devtools/httpeventcollector/
# Afterwards configure the following variables:
# 
# SPLUNK_HOST The host, e.g. an ip or a domain
# SPLUNK_PORT The port, e.g. 8088 that you configured in the splunk http event collector
# SPLUNK_TOKEN The token of your splunk http event collector
# SPLUNK_INDEX The splunk index that you want the events to end up in By default 'main'
# SPLUNK_PROTOCOL 'http' or 'https' to indicate the protocol. By default 'https'
# SPLUNK_VERIFY True or False to indicate whether to verify certificates. By default True
# SPLUNK_SOURCETYPE The source type. By default 'psono:auditLog' (that one is compatible with the provided splunk addons)
#
# More infos can be found here https://github.com/zach-taylor/splunk_handler

# If you are having Logstash running and no way to ship logs with an external agent, you can use Psono's native Logstash
# implementation to ship the logs for you. In order for that to work you need a Splunk HTTP EVent Collector to be
# configured as explained here https://dev.splunk.com/enterprise/docs/devtools/httpeventcollector/
# Afterwards configure the following variables:
# 
# LOGSTASH_HANDLER Shipping logs either async (logstash_async.handler.AsynchronousLogstashHandler) or in sync (logstash_async.handler.SynchronousLogstashHandler). By default 'logstash_async.handler.SynchronousLogstashHandler'
# LOGSTASH_TRANSPORT The transport to use. TCP: logstash_async.transport.TcpTransport or UDP: logstash_async.transport.UdpTransport or Beats logstash_async.transport.BeatsTransport or HTTP logstash_async.transport.HttpTransport. Defaults to 'logstash_async.transport.TcpTransport'
# LOGSTASH_HOST The host, e.g. an ip or a domain
# LOGSTASH_PORT The port, e.g. 5959 that you configured the. By default 5959
# LOGSTASH_SSL_ENABLED Wether you want to use SSL or not. By default True
# LOGSTASH_SSL_VERIFY True or False whether to verify certificates. By default True
# LOGSTASH_CA_CERTS If you want a custom CA, you can specify here a path to the file with the certs
# LOGSTASH_CERFILE The path to the cert file
# LOGSTASH_KEYFILE The path to the key file
#
# More infos can be found here https://python-logstash-async.readthedocs.io/en/stable/index.html

# If you want to use SAML, then you can configure it like this as a dictionary.
#
# About the parameters:
#   idp->entityId: Thats the url to the metadata of your IDP
#   idp->singleLogoutService->url: Thats the url to the logout service of your IDP
#   idp->singleSignOnService->url: Thats the url to the single sign-on service of your IDP
#   idp->x509cert: Thats the certificate of your IDP
#   idp->groups_attribute: The attribute in the SAML response that holds your groups
#   idp->username_attribute: The attribute in the SAML response that holds the username. If you put here null, then it will use the NameID
#   idp->email_attribute: The attribute in the SAML response that holds the email address.
#   idp->username_domain: The domain that is appended to the provided username, if the provided username is not already in email format.
#   idp->required_group: A list of group names (casesensitive) in order to restrict who can use SAML login with this installation. Leave empty for no restriction.
#   idp->is_adfs: If you are using ADFS.
#   idp->honor_multifactors: Multifactor authentication can be bypassed with this flag for all SAML users (e.g. when you already enforce multifactor on the SAML provider).
#   idp->max_session_lifetime: The time in seconds that a session created throught SAML will live
#
#   sp->NameIDFormat: The normal nameformat parameter. (should only be set to transient if you have set a username attribute with username_attribute)
#   sp->attributeConsumingService: Only necessary if the IDP needs to be told to send some specific attributes
#   sp->x509cert: The X.509 cert
#   sp->privateKey: The corresponding private key of the X.509 cert
#
# There are a couple of more options next to those required ones below.
# More information can be found here https://github.com/onelogin/python3-saml
#
# A self-signed certificate can be generated with:
# openssl req -new -newkey rsa:2048 -x509 -days 3650 -nodes -sha256 -out sp_x509cert.crt -keyout sp_private_key.key
#
# To help you setup SAML, we have created a small "testsaml" command that should make things easier. You can execute it like:
# docker run --rm \
#  -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
#  -ti psono/psono-combo-enterprise:latest python3 psono/manage.py testsaml
#
# The number 1 in line 2 is the provider id. Users are matched by the constructed username.
#
# SAML_CONFIGURATIONS:
#     1:
#         idp:
#             entityId: "https://idp.exampple.com/metadata.php"
#             singleLogoutService:
#                 binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
#                 url: "https://idp.exampple.com/SingleLogoutService.php"
#             singleSignOnService:
#                 binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
#                 url: "https://idp.exampple.com/SingleSignOnService.php"
#             x509cert: "ABC...=="
#             groups_attribute: "groups"
#             username_attribute: 'username'
#             email_attribute: 'email'
#             username_domain: 'example.com'
#             required_group: []
#             is_adfs: false
#             honor_multifactors: true
#             max_session_lifetime: 86400
#         sp:
#             NameIDFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
#             assertionConsumerService:
#                 binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
#             attributeConsumingService:
#                 serviceName: "Psono"
#                 serviceDescription: "Psono password manager"
#                 requestedAttributes:
#                     -
#                         attributeValue: []
#                         friendlyName: ""
#                         isRequired: false
#                         name: "attribute-that-has-to-be-requested-explicitely"
#                         nameFormat: ""
#             privateKey: "ABC...=="
#             singleLogoutService:
#                 binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
#             x509cert: "ABC...=="
#         strict: true
#
# You need a couple of urls to configure the IDP correctly. If the server is accessible under https://example.com/server
# (e.g. https://example.com/server/healthcheck/ shows some json output) and the provider id is 1 as in the example
# above the folling urls are valid:
#
# for metadata :                   https://example.com/server/saml/1/metadata/
# for assertion consumer service : https://example.com/server/saml/1/acs/
# for single logout service :      https://example.com/server/saml/1/sls/
#
#
# ATTENTION: API kays currently bypass SAML authentication, that means API keys can still access secrets even if the
# user was disabled in SAML. API keys can be disabled with COMPLIANCE_DISABLE_API_KEYS

# If you want to use OIDC, then you can configure it like this as a dictionary.
# OIDC_CONFIGURATIONS:
#     1:
#         OIDC_RP_SIGN_ALGO: 'RS256'
#         OIDC_RP_CLIENT_ID: 'whatever client id was provided'
#         OIDC_RP_CLIENT_SECRET: 'whatever secret was provided'
#         OIDC_OP_JWKS_ENDPOINT: 'https://example.com/jwks'
#         OIDC_OP_AUTHORIZATION_ENDPOINT: 'https://example.com/authorize'
#         OIDC_OP_TOKEN_ENDPOINT: 'https://example.com/token'
#         OIDC_OP_USER_ENDPOINT: 'https://example.com/userinfo'
#
# Standard parameters explained:
# OIDC_RP_SIGN_ALGO defaults to HS256 and needs to match the algo of your IDP
# OIDC_RP_CLIENT_ID the client id that is provided by your IDP
# OIDC_RP_CLIENT_SECRET the secret that is provided by your IDP
# OIDC_OP_JWKS_ENDPOINT The JWKS endpoint of your IDP
# OIDC_OP_AUTHORIZATION_ENDPOINT The authorization endpoint of your IDP
# OIDC_OP_TOKEN_ENDPOINT The token endpoint of your IDP
# 
# other parameters are:
# OIDC_VERIFY_JWT defaults to true, Controls whether Psono verifies the signature of the JWT tokens
# OIDC_USE_NONCE defaults to true, Controls whether Psono uses nonce verification
# OIDC_VERIFY_SSL defaults to true, Controls whether Psono verifies the SSL certificate of the IDP responses
# OIDC_TIMEOUT defaults to 10, Defines a timeout for all requests in seconds to the IDP (fetch JWS, retrieve JWT tokens, userinfo endpoint))
# OIDC_PROXY defaults to None, Defines a proxy for all requests to the IDP (fetch JWS, retrieve JWT tokens, Userinfo Endpoint). More infos can be found here https://requests.readthedocs.io/en/master/user/advanced/#proxies
# OIDC_RP_SCOPES defaults to 'openid email', The OpenID Connect scopes to request during login.
# OIDC_AUTH_REQUEST_EXTRA_PARAMS defaults to {}, Additional parameters to include in the initial authorization request.
# OIDC_RP_IDP_SIGN_KEY defaults to None, Sets the key the IDP uses to sign ID tokens in the case of an RSA sign algorithm. Should be the signing key in PEM or DER format.
# OIDC_ALLOW_UNSECURED_JWT defaults to False, Controls whether the Psono is going to allow unsecured JWT tokens (tokens with header {"alg":"none"}). This needs to be set to True if the IDP is returning unsecured JWT tokens and you want to accept them. See also https://tools.ietf.org/html/rfc7519#section-6
# OIDC_TOKEN_USE_BASIC_AUTH defaults to False, Use HTTP Basic Authentication instead of sending the client secret in token request POST body.

# 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',
            ],
        },
    },
]

Change the following things in this setting file:

  • Replace the first 6 lines with the secrets and keys with the values you created before.
  • Set ALLOWED_DOMAINS to your domain. This can be an internal domain for internal systems or the external public domain in case your system is reachable from the internet.
  • Set HOST_URL to the URL that will be used to access the server. The “/server” suffix must stay at the end of the URL.
  • The next thing is to configure mail settings, which may be the most challenging thing on the configuration.
    • Set EMAIL_FROM to the mail-address that will be shown as the sender.
    • Set EMAIL_HOST to the SMTP-Servers address
    • Set EMAIL_HOST_USER to the user, that is used to authenticate at the SMTP-Server.
    • Set EMAIL_HOST_PASSWORD to the users password
    • Set EMAIL_PORT to the port used. 25 is the unencrypted SMTP port. 587 is default for StartTLS, 465 is default for SSL encrypted connections.
    • Set EMAIL_SUBJECT_PREFIX to a prefix, if you like, that will be shown in front of all mail titles send by Psonso.
    • Set EMAIL_USE_TLS to True, if you use StartTLS encrypted SMTP connections. Otherwise set to False.
    • Set EMAIL_USE_SSL to True, if you use SSL encrypted SMTP connections. Otherwise set to False.
    • If you have SSL certificates for the SSL encrypted connections, set the values to a local path (i.e. /opt/docker/psono/cert.pem and /opt/docker/psono/cert.key) where you store the certificate and key for the settings EMAIL_SSL_CERTFILE and EMAIL_SSL_KEYFILE.
    • Leave EMAIL_TIMEOUT at the default value of 10 (seconds). If you have a really slow internet connections or other plausible reason to set another value, feel free to do so.
  • At the DATABASES settings part set the PASSWORD to the password you configured at the setup of the database server for the psono-user.
  • Also at DATAGBASES set the HOST value to the IP of the Database Server.
  • If you want to enable auditing: Replace the line # LOGGING_AUDIT: True with LOGGING_AUDIT: True (remove the #)
  • Later, after everything runs, you can go threw the settings file and check i.e. for LDAP settings, detailed audit settings etc.

Press Ctrl+O to write the changes and Ctrl+X to exit nano.

After setting up the settings.yaml, create a directory for the audit logs:

sudo mkdir -p /var/log/psono

Mailsettings for M365 domains

If you are using Microsoft 365 (formerly known as Office 365) to host your mails, you can follow this guide from Microsoft and use Option 2: Einrichten eines Multifunktionsgeräts oder einer Anwendung zum Senden von E-Mails mit Microsoft 365 oder Office 365 | Microsoft Learn In summary you need to set the mail settings in the settings.yaml as follows (replace the placeholders with your ones from M365):

EMAIL_FROM: '<anything>@<yourdomain>.<tld>'
EMAIL_HOST: '<your MX-Endpoint>' # i.e. contoso-com.mail.protection.outlook.com
# EMAIL_HOST_USER: ''        # No authentication
# EMAIL_HOST_PASSWORD : ''   # No authentication
EMAIL_PORT: 25               # Port 25 used for SMTP and StartTLS
EMAIL_USE_TLS: True
EMAIL_USE_SSL: False

To avoid, that the mails send are defined as Spam, you can set a SPF entry in your DNS-Settings in case you have a static IP. Add this to the TXT DNS entry that defines the SPF: v=spf1 ip4:<Static IP Address> include:spf.protection.outlook.com ~all As it varies how to set this from DNS provider to DNS provider, I cannot give you more details about how to do this.

Testing mail settings

Test, if your mails can be send without any problems. This is essential. Otherwise you cannot even start testing! Replace the testuser@testdomain.com with a valid receiver and run the following command:

sudo docker run --rm -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml -ti psono/psono-combo-enterprise:latest python3 ./psono/manage.py sendtestmail testuser@testdomain.com

Prepare the database

To fill the database with your settings run the following command:

Psono CE
sudo docker run --rm -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml -ti psono/psono-combo:latest python3 ./psono/manage.py migrate
Psono EE
sudo docker run --rm -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml -ti psono/psono-combo-enterprise:latest python3 ./psono/manage.py migrate

Create config

Create folder for the Psono client (the website) and a new configfile:

sudo mkdir /opt/docker/psono-client/
sudo nano /opt/docker/psono-client/config.json

Paste the following into the file

{
  "backend_servers": [{
    "title": "Psono.pw",
    "url": "https://psono.example.com/server"
  }],
  "base_url": "https://psono.example.com/",
  "allow_custom_server": true,
  "allow_registration": true,
  "allow_lost_password": true,
  "disable_download_bar": false,
  "authentication_methods": ["AUTHKEY", "LDAP"],
  "saml_provider": []
}

Change the content as follows:

  • title to the text that will be shown when people are accessing the Psono website
  • url to the same as HOST_URL in the settings.yaml
  • base_url to the same as url but without the /server

Press Ctrl+O to write the changes and Ctrl+X to exit nano.

Start Psono

Run the following command to start the Psono Docker container

sudo docker run --name psono-combo-enterprise \
    --sysctl net.core.somaxconn=65535 \
    -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
    -v /opt/docker/psono-client/config.json:/usr/share/nginx/html/config.json \
    -v /opt/docker/psono-client/config.json:/usr/share/nginx/html/portal/config.json \
    -v /path/to/log/folder:/var/log/psono \
    -d --restart=unless-stopped -p 10200:80 psono/psono-combo-enterprise:latest

After that try to open the following URL in your browser: http://<your server IP>:10200/server/info/. If everything is running well, you will see an output starting with {"info":"{\"version\": .... If you don’t see it, check your firewall first.

Maintenance task

To cleanup the Psono database from time to time setup a cronjob by running the following:

sudo crontab -e

if you are asked which editor you want to use, I recommend nano. Add the following line at the end:

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

Press Ctrl+O to write the changes and Ctrl+X to exit nano.

Setup Reverse Proxy

Right now Psono is running on port 80, which is not good. We will setup a reverse Proxy, that will be the endpoint for your clients and that will manage and secure the traffic with a SSL certificate to Psono. This can be done at any reverse proxy in your network. If you don’t have one, you can install one at the Psono application server as follows.

Prerequirements

You will only need a valid public certificate (.pem) and the private key (.key) for that certificate and the public certificate from the root authority. If you have a .pfx file from a Windows Certification Authority or from your certificate provider, you can extract the .pem and .key as follows:

Get the public certificate (.pem):

openssl pkcs12 -in cert.pfx -out cert.pem -nokeys -clcerts

Get the private key (.key):

openssl pkcs12 -in cert.pfx -out cert.key -nocerts -nodes

If your .pem is not a full chain certificate (i.e. because you did the extraction above), you need to add the content of your root authority certificate to the end of the .pem file. The .pem should finally looks like this (there can be some more attributes at the beginning):

-----BEGIN CERTIFICATE-----
MIIF9zCCBN+gAwIBAgITGwAAAAjgOR0bKF2AlwAAAAAACDANBgkqhkiG9w0BAQsF
...
inNyFgDzqdN8Y182+Da3iyQ4fz7pjySjyJlwk9GCJAwZLQtaudyKGInBKg==
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIQbwVSTbcyOrhIUgWXRTgKvDANBgkqhkiG9w0BAQsFADBF
...
38qV3rsb6mxhdgQ=
-----END CERTIFICATE-----

The first certificate is your public certificate and the second one is the root authority public certificate.

Copy the two files to /etc/ssl/ with sudo cp cert.pem /etc/ssl and sudo cp cert.key /etc/ssl.

Install nginx

sudo apt-get install nginx

Create site config

run the following command to create the nginx config:

sudo nano /etc/nginx/sites-available/psono.conf
server {
    listen 80;
    server_name psono.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name psono.example.com;

    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_session_timeout 1d;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

    # Comment this in if you know what you are doing
    # add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

    add_header Referrer-Policy same-origin;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    # If you have the fileserver too, then you have to add your fileserver URL e.g. https://fs01.example.com as connect-src too:
    add_header Content-Security-Policy "default-src 'none';  manifest-src 'self'; connect-src 'self' https://static.psono.com https://api.pwnedpasswords.com https://storage.googleapis.com https://*.digitaloceanspaces.com https://*.blob.core.windows.net https://*.s3.amazonaws.com; font-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'self'; child-src 'self'";

    ssl_certificate /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/privkey.key;

    client_max_body_size 256m;

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;

    root /var/www/html;

    location ~* \.(?:ico|css|js|gif|jpe?g|png|eot|woff|woff2|ttf|svg|otf)$ {
        expires 30d;
        add_header Pragma public;
        add_header Cache-Control "public";

        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        proxy_hide_header Content-Security-Policy;
        
        proxy_pass          http://localhost:10200;
        proxy_redirect      http://localhost:10200 https://psono.example.com;
    }


    location / {
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        proxy_hide_header Content-Security-Policy;
	
        proxy_pass          http://localhost:10200;
        proxy_read_timeout  90;
	
        proxy_redirect      http://localhost:10200 https://psono.example.com;
    }
}

Change the following in this file:

  • Change the two settings ssl_certificate and ssl_certificate_key to the path where the certificates are. For example:
      ssl_certificate /etc/ssl/cert.pem;
      ssl_certificate_key /etc/ssl/cert.key;
    
  • Replace psono.example.com with the domain, you will use to access this server at EVERY place. If you are accessing this server just internally, enter just the FQDN of that server from your internal domain.
  • If you have your server running internally and you are using an alias or hostname like “psono” to access the server and you want that your users are able to access psono by entering just this name into the address bar, you need to add two more entries to the top of the file, that will forward the users automatically to the FQDN of the host. Otherwise you will get the message Server offline when you try to register or logon. Add this at the beginning and replace psono with the alias/hostname you are using, FQDN with the FQDN name of your server as well as the certificate path/name: ```nginx server { listen 80; server_name psono; return 301 https://FQDN; }

server { listen 443 ssl http2; server_name psono; return 301 https://FQDN;

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
ssl_session_timeout 1d;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/cert.key; } ```

Enable site configuration

Create a symlink to the site configuration. Adjust the filename to your configfile name.

sudo ln -s /etc/nginx/sites-available/psono.example.com.conf /etc/nginx/sites-enabled/

Test configuration

Run the following command to test the nginx configuration you configured above:

sudo nginx -t

The output should tell you the test was successful. If not, it will show you what is wrong.

Apply configuration

To apply the configuration, you need to restart nginx

sudo service nginx restart

After that, you can access Psono again by trying to access the following URL in your browser: https://YourDomainOrFqdnOrHostname/server/info/. The output should start with {"info":"{\"version\":....

Setup first user and administrator

Register user

Go to https://yourDomainOrFqdnOrHostname in your browser. Click on Register and walk through the standard registration process. Finally confirm the verification mail you receive by clicking the included link. After that you should be able to logon at Psono with your registered credentials. If the logon was successful, the startup page of Psono will be shown.

Promote the first user to administrator

The first administrator must be promoted via console, as there is no administrator that can do this via GUI. To do so, run this command at the Psono application server and replace the example.com-Mail address:

sudo docker run --rm \
  -v /opt/docker/psono/settings.yaml:/root/.psono_server/settings.yaml \
  -ti psono/psono-combo:latest python3 ./psono/manage.py promoteuser username@example.com superuser

No go to https://yourDomainOrFqdnOrHostname/portal . This is the admin page of Psono. You can logon with the same credentials you used before.

You are done.

Neuste Blogposts

Install Mattermost behind OPNsense Reverse Proxy

This article is about installing mattermost behind a OPNsense reverse proxy using Ubuntu and the official Omnibus deployment method of Mattermost.

Mattermost hinter einem OPNsense Reverse Proxy installieren

Dieser Artikel beschreibt die Installation von Mattermost hinter einem OPNsense Reverse Proxy mit Ubuntu und der offiziellen Omnibus Deployment Methode von Mattermost.

Cluster Shared Volume (CSV) has redirected access

Troubleshooting guide for cluster shared volumes (CSV). From easy to hard.