Skip to content

Getting Started with OTPme

This guide walks through the initial setup of an OTPme realm and explains common day-to-day administration tasks using a practical example.

Prerequisites

Testing without hosts and clients

If you just want to try out OTPme on a single node without setting up hosts or RADIUS clients, you can initialize the realm with localhost 127.0.0.1 as the node FQDN and address. You can always migrate to a fully functional setup later.

DNS records for a production setup

When using OTPme with real hosts and clients, the following DNS records are required. The example uses the realm otpme.org, site muenchen and a floating cluster IP 192.168.1.100:

$ORIGIN _tcp.muenchen.otpme.org.
_otpme-join    SRV 10 1 2024 login.muenchen.otpme.org.
_otpme-login   SRV 10 1 2020 login.muenchen.otpme.org.
_otpme-realm   TXT "otpme.org"
_otpme-site    TXT "muenchen"

$ORIGIN muenchen.otpme.org.
login          A   192.168.1.100
node1          A   192.168.1.1
node2          A   192.168.1.2

node1, node2 etc. must match the hostnames and IPs of your actual nodes.

Load balancing

For load-balanced authentication and login requests across multiple nodes, set an auth FQDN for the site and add a round-robin DNS record for it:

otpme-site auth_fqdn muenchen auth.muenchen.otpme.org
$ORIGIN muenchen.otpme.org.
auth    A   192.168.1.1
        A   192.168.1.2

If you only have one site you can use the realm domain directly for all records (e.g. auth.otpme.org instead of auth.muenchen.otpme.org).

1. Initialize a Realm

The first step is to initialize a realm. This creates the root CA, the master site, and the first node. The positional arguments are the realm name, the site name, the FQDN of the node and (optionally) its IP address.

otpme-realm init \
    --country DE \
    --state Bayern \
    --locality Muenchen \
    --organization "OTPme" \
    --ou IT \
    --email admin@otpme.org \
    --ca-valid 3650 \
    --ca-key-len 4096 \
    --site-valid 3650 \
    --site-key-len 4096 \
    --node-valid 3650 \
    --node-key-len 4096 \
    --dicts english,en-top10000,common-passwords,us-female,us-male,us-surnames,abbreviations-it \
    --id-ranges "uidNumber:s:70000-80000,gidNumber:s:70000-80000" \
    otpme.org muenchen login.otpme.org 192.168.1.100
Option Description
--country / --state / --locality Location fields for CA certificates
--organization / --ou / --email Organisation fields for CA certificates
--ca-valid CA certificate validity in days (3650 = 10 years)
--ca-key-len CA key length in bits
--site-valid / --site-key-len Validity and key length for the site certificate
--node-valid / --node-key-len Validity and key length for the node certificate
--dicts Word dictionaries loaded into the password strength checker
--id-ranges UID/GID ranges assigned to this realm

2. Start the Daemons

otpme-controld start

3. Allow Local Management Without OTPme Login

To allow management commands when logged in to the node directly (without authenticating to OTPme), set the following option in the configuration file:

/etc/otpme/otpme.conf

USE_MGMTD_SOCKET="True"

4. Configure Management and SSO FQDNs

If you use OTPme with hosts and clients and have not initialized the realm with localhost, you should configure the management and SSO FQDNs. Both need DNS records pointing to the floating IP. The management FQDN is used by mgmtd and ldapd; the SSO FQDN is used by httpd for the SSO portal. The commands create the corresponding certificates automatically:

otpme-site mgmt_fqdn <site> <mgmt_fqdn>
otpme-site sso_fqdn <site> <sso_fqdn>

5. Configure WLAN Access

A common use case is authenticating WLAN clients via RADIUS. Create an access group and a RADIUS client (the access point). When a client is added, OTPme displays the RADIUS shared secret for that client.

otpme-accessgroup add wlan
otpme-accessgroup description wlan "WLAN Access"
otpme-accessgroup show

# Adding a client shows the RADIUS secret.
otpme-client add ap01 192.168.1.10
otpme-client access_group ap01 wlan

6. Add a User

otpme-user add alice

When a user is created, OTPme generates a default login token and displays its QR code and PIN. This token is used for realm logins and group/role membership (see later sections). You can display the user's full configuration at any time:

otpme-user show alice

7. Add a WLAN Token

Additional tokens can be created for specific services. The following creates a password token with MS-CHAPv2 support (required by most RADIUS/WLAN setups):

otpme-token --type password add --enable-mschap alice/notebook-wlan
# Set a custom password if you don't want the generated one.
otpme-token --type password password alice/notebook-wlan

8. Grant WLAN Access Directly via Token

The simplest way to grant access is to add the token directly to the access group:

otpme-accessgroup add_token wlan alice/notebook-wlan
otpme-accessgroup list_tokens wlan

# Verify authentication.
otpme-auth verify --socket alice <password> ap01

Directly assigning tokens to access groups does not scale well. Using roles makes it easy to grant or revoke access for groups of users at once. Remove the direct token assignment and use a role instead:

# Remove direct token assignment.
otpme-accessgroup remove_token wlan alice/notebook-wlan
# Confirm that access is gone.
otpme-auth verify --socket alice <password> ap01

# Create a role and assign it to the access group.
otpme-role add wlan-user
otpme-accessgroup add_role wlan wlan-user

# Add the token to the role.
otpme-role add_token wlan-user alice/notebook-wlan

# Confirm that access is working again.
otpme-auth verify --socket alice <password> ap01

10. Restrict Login Times with Policies

Policies can restrict when logins are allowed. The built-in workhours_login policy (and the weekend_login policy) are good examples. Policies can be applied to access groups or directly to users:

# Restrict logins via the access group.
otpme-accessgroup add_policy wlan workhours_login
otpme-auth verify --socket alice <password> ap01
otpme-accessgroup remove_policy wlan workhours_login

# Restrict logins via the user.
otpme-user add_policy alice workhours_login
otpme-auth verify --socket alice <password> ap01
otpme-user remove_policy alice workhours_login

11. Disable and Enable Users and Tokens

Disabling a user prevents all logins for that user. Individual tokens can also be disabled independently:

# Disable/enable a user.
otpme-user -f disable alice
otpme-auth verify --socket alice <password> ap01
otpme-user -f enable alice

# Disable/enable a single token.
otpme-token -f disable alice/notebook-wlan
otpme-auth verify --socket alice <password> ap01
otpme-token -f enable alice/notebook-wlan

12. Login Failure Limits

You can configure a maximum number of failed login attempts per access group. After the limit is reached the user is blocked for that access group:

otpme-accessgroup max_fail wlan 3

# Three failed attempts will block the user.
otpme-auth verify --socket alice $RANDOM ap01
otpme-auth verify --socket alice $RANDOM ap01
otpme-auth verify --socket alice $RANDOM ap01
otpme-user show alice

# Unblock the user manually.
otpme-user unblock alice wlan

# Or configure an automatic unblock after a timeout.
otpme-accessgroup max_fail_reset wlan 1m

13. Auto Disable

Users and tokens can be configured to disable themselves automatically after a given time. This is useful for temporary accounts or time-limited access:

# Disable alice automatically after 1 minute (for testing).
otpme-user auto_disable alice +1m
# Wait a minute, then check.
otpme-user show alice

# Remove the auto disable.
otpme-user auto_disable alice 0
otpme-user -f enable alice

Time suffixes: m = minutes, h = hours, D = days, W = weeks.

14. Groups

Groups map to POSIX groups on the node. The default group of a user becomes their primary group. It takes a moment for otpme-hostd to sync changes to system users; you can trigger a manual sync with otpme-tool sync:

# Create a management group and make it alice's default group.
otpme-group add management
otpme-user group alice management
otpme-user show alice

otpme-tool sync
id alice

# Add a staff group and add alice's login token to it.
otpme-group add staff
otpme-group add_token staff alice/login
otpme-tool sync
id alice

# Inspect the group.
otpme-group show staff$
otpme-group list_tokens staff
otpme-group list_users staff

Managing group membership via tokens directly does not scale well. Using roles allows you to control group membership for many users at once:

# Remove the direct token assignment.
otpme-group remove_token staff alice/login
otpme-tool sync
id alice

# Create a role and assign it to the group.
otpme-role add management-user
otpme-group add_role staff management-user

# Add alice's login token to the role.
otpme-role add_token management-user alice/login
otpme-tool sync
id alice

# Inspect the role.
otpme-role show management-user$
otpme-role list_tokens management-user
otpme-role list_users management-user

Nested Roles

Roles can contain other roles. This is useful when you have multiple roles (e.g. management-user, marketing-user) and want all of them to share a common group membership via a single staff role:

# Remove the direct role-to-group assignment.
otpme-group remove_role staff management-user
otpme-tool sync
id alice

# Add a staff role.
otpme-role add staff
# Assign the staff role to the group.
otpme-group add_role staff staff
# Make management-user a member of the staff role.
otpme-role add_role staff management-user

Alice now belongs to the staff group because her management-user role is a member of the staff role which is assigned to the staff group.

To also ensure that all management users are members of the management group, add the role directly to that group as well:

otpme-group add_role management management-user

16. Units

Units are organisational containers for users, groups and other objects (tokens are bound to users, not to units). They can be used to delegate administration — e.g. allowing department executives to manage users within their own unit:

otpme-unit add management
otpme-unit add management/users
otpme-unit add management/groups

# Move user and group into the appropriate units.
otpme-user move alice management/users
otpme-group move management management/groups

17. Delegate Unit Administration

To allow a dedicated manager user to administer objects within a unit, create the manager user inside the unit and grant them the necessary ACLs.

The -r flag on add replaces an existing token (useful when re-running the setup). For easy testing the password is set to a fixed value here.

# Add management manager user joe.
otpme-user add management/users/joe
otpme-token --type password add -r joe/login
otpme-token -f --type password password joe/login password

Grant joe the right to create, delete and edit users and their tokens within the management/users unit. The + prefix means the ACL is inherited within the unit, ++ means it is inherited recursively into sub-units:

otpme-unit add_acl management/users token joe/login "add:user"
otpme-unit add_acl management/users token joe/login "delete:user"
otpme-unit add_acl management/users token joe/login "+user:edit"
otpme-unit add_acl management/users token joe/login "+user:enable"
otpme-unit add_acl management/users token joe/login "+user:disable"
otpme-unit add_acl management/users token joe/login "+user:add:attribute"
otpme-unit add_acl management/users token joe/login "+user:add:token"
otpme-unit add_acl management/users token joe/login "+user:delete:token"
otpme-unit add_acl management/users token joe/login "+user:delete"
otpme-unit add_acl management/users token joe/login "+token:edit"
otpme-unit add_acl management/users token joe/login "+token:enable"
otpme-unit add_acl management/users token joe/login "+token:disable"

Allow joe to set the default group of users to management and to add/remove tokens from the management-user role:

# Allow joe to add/remove users to/from the management default group.
otpme-group add_acl management token joe/login "add:default_group_user"
otpme-group add_acl management token joe/login "remove:default_group_user"
# Allow joe to add/remove tokens from the management-user role.
otpme-role add_acl management-user token joe/login "add:token"
otpme-role add_acl management-user token joe/login "remove:token"

With these ACLs in place, joe can now add users to the unit and assign them to the group and role directly at creation time using --group and --role:

# Login as joe and add a test user.
otpme-tool login joe
otpme-user add --group management --role management-user management/users/user1
otpme-token --type password add user1/wlan
otpme-tool logout

18. Simplify User Creation with Policies

Having to specify --group and --role on every otpme-user add call is tedious. Policies can automate these assignments so that new users created within a unit get the right group and role automatically.

First, create a unit for the policies:

otpme-unit add management/policies

Default Groups Policy

Configure that new users in the management/users unit automatically get management as their default group:

# Add default groups policy.
otpme-policy --type defaultgroups add management/policies/management-groups
# Configure that the default group of new users will be management.
otpme-policy --type defaultgroups default_group management-groups management
# Remove the existing default policy from the unit.
otpme-unit remove_policy management/users default_groups
# Add the new default groups policy to the unit.
otpme-unit add_policy management/users management-groups

Default Roles Policy

Configure that new users in the unit are automatically added to the management-user role:

# Add default roles policy.
otpme-policy --type defaultroles add management/policies/management-roles
# Configure that new users will be added to the management-user role.
otpme-policy --type defaultroles add_default_role management-roles management-user
# Remove the existing default policy from the unit.
otpme-unit remove_policy management/users default_roles
# Add the new default roles policy to the unit.
otpme-unit add_policy management/users management-roles

Remove Object Templates Policy

If object templates are not needed, remove that policy from the unit:

otpme-unit remove_policy management/users object_templates

Default Units Policy

To allow joe to add users without having to specify the target unit each time, assign a defaultunits policy directly to joe:

# Add default units policy.
otpme-policy --type defaultunits add management/policies/management-units
# Set the default unit for new users.
otpme-policy --type defaultunits set_unit management-units user management/users
# Add the policy to user joe.
otpme-user add_policy joe management-units

Joe can now create users with a simple otpme-user add <username> and they will automatically land in management/users, get management as their default group and be assigned the management-user role.

19. Joining a Host to the Realm

Before a host can authenticate users via OTPme, it must be joined to the realm. The join command must always be run as root on the host itself. It creates the host object in the realm automatically.

otpme-tool join

Joining as an Ordinary User

To allow a non-root user like joe to join hosts, grant the necessary ACLs on the JOIN access group and the hosts unit:

otpme-accessgroup add_acl JOIN token joe/login "join:host"
otpme-accessgroup add_acl JOIN token joe/login "leave:host"
otpme-unit add_acl hosts token joe/login "add:host"

Joe can then join a host on the host itself:

# First remove any existing host object.
otpme-tool leave            # run on the host
otpme-host del <yourhostname>   # run on a node
# Now join as joe.
otpme-tool -u joe join

Joining via JOTP

An alternative is to pre-create the host object in the realm first. This is useful when you want to let a user join one specific host without granting the general right to join any host. When a host object is created, a JOTP (join one-time password) is displayed. The host can then join using that JOTP:

# Remove existing host object and re-create it to get a fresh JOTP.
otpme-tool leave
otpme-host del <yourhostname>
otpme-host add <yourhostname>
# On the host, join using the JOTP. A LOTP is displayed which can be used to leave.
otpme-tool join --jotp <jotp>

Allow Users to Log in to a Host

By default, login to a host is restricted: only tokens explicitly assigned to the host (directly or via a role) are permitted, and only users with such tokens are synced to the host. To allow joe to log in, assign his token:

otpme-host add_token <yourhostname> joe/login

Trigger a sync on the host and verify the user is available:

otpme-tool sync
id joe
otpme-tool login joe

Unlimit Logins

To allow all realm users to log in and be synced to a host, remove the login restriction entirely:

otpme-host unlimit_logins <yourhostname>

Controlling Which Users Are Synced

Without unlimiting logins, there are several ways to control which users get synced to a host:

# Add a specific user as a sync user directly on the host.
otpme-host add_sync_user <yourhostname> <username>

# Add sync users to a role — all role members will be synced to hosts
# that have the role assigned.
otpme-role add_sync_user management-user alice

# Use a dedicated sync group.
otpme-group add mysyncgroup
otpme-group add_sync_user mysyncgroup alice
otpme-host add_sync_group <yourhostname> mysyncgroup

# Finally enable sync groups for the host.
otpme-host enable_sync_groups <yourhostname>

# If you use sync groups you may want to disable sync-by-login-token.
otpme-host disable_sync_by_login_token <yourhostname>

20. Configure the PAM Module

To enable OTPme logins on a host, the PAM module must be deployed. Copy the module and the OTPme PAM configuration files into place:

# Copy the PAM module.
cp /opt/otpme/lib/python3.11/site-packages/etc/otpme/deploy/pam/pam-python/pam_otpme.py \
    /lib/security/
# Copy OTPme PAM configuration files.
cp /opt/otpme/lib/python3.11/site-packages/etc/otpme/deploy/pam/otpme-* /etc/pam.d/

Then edit the PAM file for the service you want to protect (e.g. /etc/pam.d/login). Comment out the default includes and add the OTPme ones:

# Comment out the default includes:
#@include common-auth
#@include common-account
#@include common-session

# Add OTPme includes:
@include otpme-auth
@include otpme-account
@include otpme-session

You should now be able to log in as user joe from any TTY. To test, use login and monitor /var/log/syslog for details:

login joe

21. Offline Logins

Offline login support must be enabled per token. Once enabled, the token credentials are cached locally after a successful online login and can be used when the OTPme server is unreachable.

otpme-token enable_offline joe/login

With session keeping enabled, the login session is additionally cached and automatically loaded into otpme-agent on the next offline login:

otpme-token enable_session_keep joe/login

After logging in online as joe, verify that the offline token was cached:

otpme-tool show_offline_token

For hosts with special security requirements, offline tokens can be pinned. This ensures that only login with the pinned token is possible, preventing even an administrator with server access from authorising a login with a different token:

otpme-tool pin_offline_token

Restrict Which Users May Log In

The following options in /etc/otpme/otpme.conf control which users are allowed to log in to a specific host:

# Allow only the listed users to log in.
VALID_LOGIN_USERS="joe,alice"

# Allow all users except the listed ones.
DENY_LOGIN_USERS="alice"

22. Dynamic Groups

Dynamic groups allow OTPme to add users to local POSIX groups (via setgroups()) at login time — for example to grant access to plugdev for USB device handling. They can be configured on hosts, tokens and roles.

# Add all users that log in to this host to the plugdev group.
otpme-host add_dynamic_group <yourhostname> plugdev

# Add to plugdev for users authenticating with a specific token.
otpme-token add_dynamic_group joe/login plugdev

# Add to plugdev for all members of a role.
otpme-role add_dynamic_group management-user plugdev

For hosts with strict security requirements, dynamic group assignment can be restricted in /etc/otpme/otpme.conf:

# Only allow assignment to the listed dynamic groups.
ALLOW_DYNAMIC_GROUPS="plugdev"

# Set to False to disable dynamic groups entirely on this host.
ALLOW_DYNAMIC_GROUPS="False"

# Leave empty to allow all dynamic groups except those listed in DENY_DYNAMIC_GROUPS.
ALLOW_DYNAMIC_GROUPS=""

# Block specific dynamic groups.
DENY_DYNAMIC_GROUPS="wheel"

23. Extending the Cluster

To achieve high availability and load balancing you can add more nodes to the OTPme cluster. Install OTPme on the additional server (see README) and run the join command with --host-type node:

otpme-tool join --host-type node

After joining, the new node starts syncing with the master node (check /var/log/otpme/otpme-clusterd.log). To verify the cluster status:

otpme-cluster status

If the cluster is not fully in sync (indicated by coloured lines in the output), use the --diff option to show the differences between nodes:

# Show object sync checksum differences.
otpme-cluster status --diff

# Compare full object data (may produce high load and take some time).
otpme-cluster status --diff --full

# Compare full index object data (may produce high load and take some time).
otpme-cluster status --diff --full-index

The OTPme cluster always has a master node which holds the floating IP configured during realm init. All management commands are executed on the master node. To failover the master role to another node, log in to the target node and run:

otpme-cluster master_failover --wait

24. Fileserver and Shares

OTPme provides fileserver capabilities using FUSE mounts on the client side. Files and directories in the share root (default: /otpme-mounts/, see otpme-site show_config <sitename> share_root) must be available on all cluster nodes — using CephFS is recommended, but a central NFS mount also works.

Share Scripts

When a new share is added, OTPme runs a configurable add script (otpme-site show_config <sitename> default_share_add_script). The script receives $share_name, $share_root, $force_group, $force_create_mode and $force_directory_mode as environment variables and runs as the OTPme system user (otpme:otpme), so sudo(1) may be needed for privileged operations. Storing the actual script under /otpme-mounts makes it available on all nodes.

# Edit the share add script (opens with $EDITOR).
otpme-script edit scripts/add_share.sh

There is also a mount script (otpme-site show_config <sitename> default_share_mount_script) that runs when a client mounts a share. It receives $share_name and $share_root:

# Edit the default mount script.
otpme-script edit scripts/mount_share.sh

# Override the mount script for a specific share.
otpme-share mount_script testshare scripts/mount_share_special.sh

To change the default scripts site-wide (can also be set per unit):

otpme-site config <sitename> default_share_add_script scripts/share_add_special.sh
otpme-site config <sitename> default_share_mount_script scripts/share_mount_special.sh

Adding a Share

The share root directory must exist before creating the share (unless the add script handles this):

mkdir /otpme-mounts/testshare
otpme-share add testshare

Shares support three force options that control ownership and permissions for newly created files and directories:

otpme-share add \
    --force-group management \
    --force-directory-mode 0o770 \
    --force-create-mode 0o660 \
    testshare

These options can also be changed on an existing share:

otpme-share force_group testshare testgroup
otpme-share force_create_mode testshare 0o660
otpme-share force_directory_mode testshare 0o770

Accessing a Share

To allow a user to access the share, add a token or a role:

otpme-share add_token testshare joe/login

To test share access, log in on a host or node and mount all shares. They will appear under /otpme/<site>/<share>:

# Mount all shares.
otpme-mount -a

# Unmount all shares.
otpme-mount -a -u

To automatically mount shares when a user logs in via the PAM module, enable auto-mount for the user:

otpme-user enable_auto_mount joe

A share can be made read-only, and it can be disabled entirely (which refuses any new mount request — already mounted shares continue to work):

otpme-share enable_ro testshare
otpme-share disable testshare

Encrypted Shares

OTPme supports encrypted shares where file content and path names are encrypted on the client side. Every user who needs access to the share (including the creating user) must have a public/private key pair:

otpme-user gen_keys root
otpme-user gen_keys joe

Create an encrypted share — you will be asked for a master password. Store this password in a secure place, as it is needed to decrypt the share if the last token is removed and no share keys are available any more:

otpme-share add --crypt testcrypt

Using a Yubikey for Key Management

By default the private key created with gen_keys is encrypted by the key script (scripts/key_script.sh) and stored in the user object on the nodes. A more secure alternative is to use a Yubikey in PIV mode. The following command replaces (-r) the user's default token so that the user can only log in on hosts where the PAM module is configured and the Yubikey is connected. The --add-user-key option adds the public key to the token user:

# WARNING: This resets the PIV applet and erases all PIV keys on the Yubikey.
otpme-token --type yubikey_piv deploy --add-user-key -r joe/login

# To use a Yubikey that already has a private key configured, add -n:
otpme-token --type yubikey_piv deploy --add-user-key -n -r joe/login

To use the Yubikey in PIV mode with the key script, configure the user's key script options. The --use-agent-piv option delegates PIV operations to the otpme-agent so you do not have to enter the PIV PIN on every operation:

otpme-user key_script joe scripts/key_script.sh -- --yubikey-piv AUTHENTICATION --use-agent-piv

To add the Yubikey PIV key to the ssh-agent on login via the PAM module, configure the agent script with --yubikey-piv. By default the key is added with confirmation enabled (ssh-askpass is called on each use). To disable confirmation add --no-confirm:

otpme-user agent_script joe scripts/agent_script.sh -- --yubikey-piv --no-confirm

25. Encrypting, Decrypting and Signing Files

After logging in as a user with a key pair (see above), you can use otpme-tool to encrypt, decrypt, sign and verify files:

# Create a test file.
echo test > test.txt

# Encrypt the file.
otpme-tool encrypt test.txt test.txt.enc

# Decrypt the file to stdout.
otpme-tool decrypt test.txt.enc /dev/stdout

Signing and verifying works similarly:

# Sign a file.
otpme-tool sign test.txt test.txt.sig

# Verify the signature.
otpme-tool verify test.txt.sig test.txt

Script Signature Verification

Signing can also be used to sign OTPme scripts such as the agent script. A host can then be configured to verify script signatures before executing them. To sign the agent script with the root user on the master node:

otpme-script sign scripts/agent_script.sh

On the host that should verify the agent script signature, add root as a trusted signer. After this, the PAM module will verify the script signature on login — if no valid signature from user root exists, the script will not run:

otpme-tool add_signer --signer-type agent_script "user|realm/root"

# Show configured signers.
otpme-tool show_signer

Token Signature Verification (SSH)

Besides scripts, it is also possible to sign tokens. Currently SSH tokens are supported (note: YubiKey PIV tokens are also SSH tokens). This allows a host to only accept SSH public keys that have been signed by a trusted user.

First, add an SSH token and assign it to the host:

# Add SSH token with the user's public key.
otpme-token --type ssh add joe/ssh "<ssh_public_key>"

# Add the token to the host.
otpme-host add_token <yourhostname> joe/ssh

# Trigger sync and verify the key is available.
otpme-tool sync
otpme-get-authorized-keys joe

Now add a token signer on the host. After this, only signed SSH keys will be accepted:

# Add token signer (run on host as root).
otpme-tool add_signer --signer-type token "user|realm/root"
otpme-tool sync

# The SSH key should no longer be shown — it is not signed yet.
otpme-get-authorized-keys joe

Sign the SSH token on the master node, then sync again:

# Sign SSH token (run on master node).
otpme-token --type ssh sign joe/ssh

# Trigger sync on the host.
otpme-tool sync

# Now the SSH public key should be shown — it is signed by root.
otpme-get-authorized-keys joe

26. SSH Login Configuration

To allow SSH logins using OTPme-managed authorized keys, add the following to your SSH configuration (e.g. /etc/ssh/sshd_config.d/otpme.conf):

AuthorizedKeysCommand /opt/otpme/bin/otpme-get-authorized-keys
AuthorizedKeysCommandUser root
PermitUserEnvironment yes

27. TOTP Login and Second Factor Authentication

If you do not have a hardware token or smartcard, you can use a TOTP token with any TOTP authenticator app (e.g. Google Authenticator). Add a TOTP token and scan the displayed QR code with your authenticator app:

# Add TOTP token.
otpme-token --type totp add joe/totp

# Allow realm logins with the TOTP token.
otpme-role add_token REALM_USER joe/totp

# Allow login to your host.
otpme-host add_token <yourhostname> joe/totp

After syncing (otpme-tool sync or waiting for SYNC_INTERVAL (default 180s, configurable in /etc/otpme/otpme.conf)), enter PIN+OTP at the login prompt.

Using TOTP as Second Factor

For offline logins it is recommended to use a password token with the TOTP token as second factor. This way the offline token/session data is encrypted with the password+PIN, which is more secure than encrypting it with just the TOTP PIN:

# Add password token.
otpme-token --type password add joe/pass

# Set TOTP token as second factor of the password token.
otpme-token --type password 2f_token joe/pass totp

# Enable second factor authentication.
otpme-token --type password enable_2f joe/pass

# Enable offline token and session keeping.
otpme-token enable_offline joe/pass
otpme-token enable_session_keep joe/pass

# Allow realm logins with the password token.
otpme-role add_token REALM_USER joe/pass
otpme-host add_token <yourhostname> joe/pass

Since the TOTP token is now used as a second factor of the password token, the standalone TOTP permissions are no longer needed:

otpme-role remove_token REALM_USER joe/totp
otpme-host remove_token <yourhostname> joe/totp

After syncing (otpme-tool sync or waiting for SYNC_INTERVAL (default 180s, configurable in /etc/otpme/otpme.conf)), enter password+PIN+OTP at the login prompt.

28. Confirmation Policy

OTPme asks for confirmation when changing or deleting objects. This behaviour is controlled by the confirmation_policy config parameter:

  • paranoid — ask for confirmation on all changes; deleting requires typing the object name (default)
  • normal — only ask when deleting objects; requires typing the object name to confirm
  • force — never ask for confirmation

The -f command line option also skips all confirmations regardless of the policy.

The policy can be set on the site (applies globally), on a unit or on individual tokens:

otpme-site config <site> confirmation_policy force

29. 802.1x and MAB Port Authentication

OTPme supports 802.1x and MAC Authentication Bypass (MAB) port authentication. To use either method, configure your switch to send RADIUS requests to the OTPme nodes.

Both MAB and 802.1x require an access group and a client for the switch:

# Create an access group for LAN access.
otpme-accessgroup add lan

# Add the switch as an OTPme client. If the switch sends a NAS-Identifier
# the client name must match it; otherwise use the switch IP.
otpme-client add switch1 192.168.1.20

# Assign the access group to the client.
otpme-client access_group switch1 lan

MAB (MAC Authentication Bypass)

For MAB, add the host's MAC address to the host object and then add the host to the access group. You can also add hosts to roles and roles to the access group:

otpme-host mac <yourhostname> 90:1b:0e:46:46:15
otpme-accessgroup add_host lan <yourhostname>

You can define a default access group that new hosts are automatically added to:

otpme-site config <site> hosts_accessgroup lan

MAB for Network Devices (IP Phones etc.)

For non-host devices such as IP phones or printers, use the otpme-device command instead. You can also add devices to roles and roles to the access group:

otpme-device add <devicename>
otpme-device mac <devicename> 90:1b:0e:46:46:16
otpme-accessgroup add_device lan <devicename>

Similarly to hosts, you can define a default access group for new devices:

otpme-site config <site> devices_accessgroup lan

VLAN Assignment

To assign a VLAN during MAB or 802.1x authentication, set the vlan config parameter. It can be set at different levels — the most specific match wins:

# VLAN based on token authentication.
otpme-token config joe/login vlan <vlan_name>
# VLAN based on host or device.
otpme-host config <yourhostname> vlan <vlan_name>
otpme-device config <devicename> vlan <vlan_name>

802.1x Authentication

For 802.1x you do not need to add the host to the access group. Instead, add the user's token to the access group. Currently only password and yubikey_piv tokens support 802.1x authentication:

otpme-accessgroup add_token lan joe/login

You also need to enable 802.1x authentication on the client:

otpme-client enable_dot1x switch1

On the host side, enable 802.1x in the PAM module configuration. The relevant PAM options are:

  • do_dot1x=auto|forceauto performs dot1x only when the default gateway is unreachable; force always performs dot1x on login (restarts the network connection)
  • dot1x_connection — Network Manager connection to use for 802.1x (defaults to dot1x-lan)
  • dot1x_token_type — token type used for 802.1x OTP generation (e.g. yubikey_piv, password)
  • dot1x_timeout — seconds to wait for 802.1x authentication to complete

A typical PAM configuration line:

auth [success=1 default=ignore] pam_python.so pam_otpme.py try_first_pass \
    do_dot1x=force dot1x_token_type=yubikey_piv dot1x_timeout=10 \
    cache_login_tokens use_smartcard=auto use_ssh_agent=auto start_ssh_agent=auto \
    check_offline_pass_strength=password_strength;0:6,1:6,2:6,3:5,4:5,5:5,6:3,7:3,8:3,9:3,10:3

Before enabling dot1x, configure a Network Manager dummy connection on the host. The user/password values are placeholders and will be set by the PAM module on login. ca-cert and domain-suffix-match are required so the supplicant validates the RADIUS server before sending credentials. All OTPme nodes share the site certificate whose SAN is <site>.<realm>, so one domain-suffix-match covers every RADIUS node:

nmcli connection add type ethernet con-name "dot1x-lan" ifname enp0s25 \
    802-1x.eap ttls \
    802-1x.phase2-auth pap \
    802-1x.ca-cert /etc/otpme/ssl/ca.pem \
    802-1x.domain-suffix-match <site>.<realm> \
    802-1x.anonymous-identity "anonymous@<realm>" \
    802-1x.identity "user" \
    802-1x.password "pass"

30. Trash

OTPme includes a trash that keeps deleted objects. Every object deleted via a tree command (e.g. otpme-user del) is moved to the trash instead of being permanently removed:

# Add and delete a test user.
otpme-user add user1
otpme-user del user1

# Show trash contents — user1 and its token are now in trash.
otpme-trash show

Objects can be restored by their trash ID:

# Restore object from trash.
otpme-trash restore <trash_id>

# Restore but keep a copy in the trash.
otpme-trash restore --keep <trash_id>

# Restore only specific objects from a trash entry.
otpme-trash restore --objects "user|realm/site/users/user1" <trash_id>

To permanently remove all objects from the trash:

otpme-trash empty

31. Backup and Restore

OTPme includes a backup tool that exports all OTPme data (excluding trash) to a given directory. Additionally you must back up /etc/otpme to make a full restore possible:

otpme-tool backup -d /var/backups/otpme

To perform a disaster recovery, make sure /etc/otpme is restored first, then run:

otpme-tool restore -d /var/backups/otpme/<snapshot>

If you only need to restore a single object, specify the backup file directly:

otpme-tool restore -f /var/backups/otpme/<snapshot>/user/realm+user1.json

32. Integrated Deduplication Backup Service

OTPme includes a built-in deduplication backup service. Any node or host can act as the backup server. There are two backup modes:

  • pack — backup data (chunks) is written to pack files of approximately 512 MB each. File and directory metadata is stored in an SQLite database. This is the default for nodes and more space-efficient.
  • tree — in addition to pack files, the full directory tree including files is stored on the backup server. This allows mounting the backup repository from any host (e.g. via a restore share). This is the default for shares.

Backup Server Configuration

On the host that will act as backup server, add the following settings to /etc/otpme/otpme.conf and restart the daemons (otpme-controld restart):

# Enable the backup service on this host.
BACKUP_SERVER="True"
# Root directory for all backup repositories.
BACKUP_DIR="/var/backups"
# Allow new repositories from these client certificate CNs (comma-separated, or False).
ALLOW_NEW_BACKUP_REPOS="node1.site.realm,node2.site.realm"

Backup Configuration Parameters

On the master node, all backup settings are managed via config parameters. They can be set on sites, units, nodes, and shares (unless noted otherwise):

Parameter Description
backup_enabled Enable or disable backups (True/False)
backup_interval Backup interval (e.g. 1D, defaults to 24h)
backup_key AES backup key (64-character hex string; auto-generated if not set)
backup_repo_password Repository password
backup_script OTPme backup script (e.g. scripts/backup_script.sh)
backup_server Backup node or host name (e.g. backup-host)
backup_time Time window for starting backups (e.g. 22:00-05:00)
backup_excludes Directories/files to exclude (nodes and shares only)
backup_includes Directories/files to include (nodes and shares only)
backup_exclude_special Exclude special files such as device files
backup_mode pack or tree (nodes and shares only)
# Enable backup for all nodes and shares.
otpme-site config <site> backup_enabled True
# Set backup time window.
otpme-site config <site> backup_time "22:00-05:00"
# Set backup interval.
otpme-site config <site> backup_interval 1D
# Add an extra backup exclude for a specific node.
otpme-node config -a <node> backup_excludes some/dir
# Generate a site-wide backup key.
otpme-site config <site> backup_key
# Set backup server.
otpme-site config <site> backup_server backup-host

Running Backups

You can test a backup in dry-run mode before running it for real:

# Dry run for a node.
otpme-backup start --dry-run node:node1
# Dry run for a share.
otpme-backup start --dry-run share:testshare
# Start backup with debug output to see progress.
otpme-backup -dd start node:node1

Listing and Restoring Backups

# List available snapshots.
otpme-backup list node:node1
# List contents of a snapshot.
otpme-backup ls node:node1 2026-04-03T23-00-26 usr/bin
# Restore a specific path from a snapshot.
otpme-backup -dd restore --snapshot 2026-04-03T23-00-26 --path usr/bin --destination /tmp/restore node:node1

Restore Shares

When a share has been backed up in tree mode, you can create a restore share that allows mounting the backup repository on any host — including for encrypted shares:

otpme-share add --restore testshare --restore-token joe/login testshare-restore