multiOTP
Overview
multiOTP is an open-source, OATH-certified strong two-factor authentication server. It issues and validates time-based and counter-based one-time passwords (TOTP and HOTP) as well as Mobile-OTP, provisions users to authenticator apps with QR codes, and keeps its users and tokens in a simple local flat-file store.
This SecureAnyCloud image runs the open-source LGPL edition of multiOTP 5.10.2.2 behind nginx with PHP-FPM 8.3. It ships only the open-source edition, never the commercial multiOTP Pro/Enterprise appliance. You manage it from a command-line utility or the bundled web administration console, and it integrates with RADIUS front ends (such as FreeRADIUS), PAM and VPN login flows to add a second factor to your existing authentication.
What you get:
- multiOTP 5.10.2.2 open-source edition (LGPL-3.0): the CLI (
multiotp) plus the bundled web admin console - nginx and PHP-FPM 8.3 with OPcache and every PHP extension multiOTP requires
- OATH-certified TOTP, HOTP and Mobile-OTP with QR-code provisioning and a flat-file user and token store
- Secure by default: nginx HTTP Basic authentication fronts the web admin console, and the multiOTP administrator account is rotated in lock-step with it
- A per-VM administrator password, generated at first boot and applied to both the nginx Basic-auth gate and the multiOTP admin account. It is written to
/root/multiotp-credentials.txtwith mode0600, so only the root user can read it - No shared credentials: two VMs deployed from the same image never share a password, and no default
admin/1234login ever ships in the image - An unauthenticated
/healthendpoint for load balancers and monitors. Every other path requires authentication - 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet and subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point. Scale up for larger user directories.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network. Also allow 80/tcp (plus 443/tcp once you enable HTTPS) from the networks that reach the admin console and query the authentication service.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for multiOTP by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).
Select Review + create, then Create. First-boot initialisation takes about a minute after the VM starts.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:multiotp-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name multiotp \
--image sac1647283583153:multiotp-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name multiotp --port 80 --priority 1010Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>Step 4 - Verify the multiOTP services
The web stack is two systemd services, nginx and PHP-FPM. Confirm that both are active, that nginx is listening on port 80, and that the unauthenticated health endpoint answers:
systemctl is-active nginx php8.3-fpm
sudo ss -tlnp | grep ':80 ' curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/healthBoth service lines should read active, nginx should be listening on 0.0.0.0:80, and the health check should return HTTP 200.
Step 5 - Retrieve the per-VM administrator password
The first boot of every VM generates a unique administrator password and writes it to a root-only file. Read it with:
sudo cat /root/multiotp-credentials.txtThe multiotp.admin.pass value is unique to this VM. The same admin / password pair unlocks both the nginx Basic-auth prompt and the multiOTP web admin sign-in.
Step 6 - Sign in to the web admin console
Browse to http://<vm-public-ip>/. Your browser first shows an HTTP Basic authentication prompt. Enter the user admin and the password from Step 5.
You then reach the multiOTP web administration console. Sign in there with the same user admin and password.
From the console you can add users, import hardware tokens, resync and check users, and back up or restore the configuration. The Add a new user panel enrols a user and issues a soft or hardware token:
The token list shows every enrolled user with actions to print a provisioning sheet, generate a scratch list or resync a token:
Step 7 - Enrol a user and provision a token from the CLI
You can also manage users from the command line. The multiotp command runs as the web service account, so users created on the CLI are immediately visible in the web console.
Create a user without a prefix PIN, print its provisioning link, and write a QR code that a phone can scan. Write the QR code to a world-readable path such as /tmp:
$
sudo multiotp -fastcreatenopin alice INFO: user alice successfully created (exit 11) $
sudo multiotp -urllink alice otpauth://totp/multiOTP:alice?secret=PKTDTQG7O5W4KZ7MHX...&digits=6&period=30 $
sudo multiotp -qrcode alice /tmp/alice-qr.png QR provisioning code written to /tmp/alice-qr.pngScan the QR code with an authenticator app (Google Authenticator, FreeOTP, Aegis, Microsoft Authenticator, ...).
multiOTP management commands return an informational non-zero exit code on success (for example 11 = user created). A zero exit code from the validation command below signals an accepted one-time password.
Step 8 - Validate a one-time password
To authenticate a user, pass their current one-time password to multiotp. It exits 0 when the code is accepted and non-zero when it is rejected. This is the contract you wire into a RADIUS, PAM or VPN login flow:
$
sudo multiotp alice 819241 OK: token accepted (exit 0) $
sudo multiotp alice 000000 ERROR: wrong one-time password (exit 99)You can also validate interactively from the Check a user panel in the web console:
Step 9 - Enable HTTPS (recommended)
The image serves the admin console over HTTP on port 80. For production, put it behind HTTPS. Point a DNS name at the VM, open 443/tcp, and use Let's Encrypt:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comcertbot obtains a certificate and reconfigures nginx to redirect HTTP to HTTPS. The packaged systemd timer renews the certificate automatically.
Step 10 - Integrate with RADIUS, PAM or VPN
multiOTP is designed to sit behind an authentication front end. A common pattern is FreeRADIUS calling multiOTP as an external check, so that VPNs, Wi-Fi and network devices gain a second factor.
Point your front end at the multiotp command (exit code 0 = accept) or run the bundled network web service. See the multiOTP project documentation for FreeRADIUS and PAM integration recipes.
Maintenance
- Backups: the configuration, users and tokens live under
/etc/multiotp. Use the web console's Backup the configuration panel, or archive/etc/multiotpoff the VM. - Updates: the base OS has unattended security upgrades enabled. Keep the VM patched with
sudo apt-get update && sudo apt-get upgrade. - Credentials: re-read
/root/multiotp-credentials.txtat any time to recover the initial administrator password. Change it from the console's Change admin password panel.
Support
This image is maintained by SecureAnyCloud with 24/7 support. For help with deployment or configuration, contact sales@secureanycloud.com.
Deploy on Azure
Launch multiOTP on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
multiOTP is an open-source, OATH-certified strong-authentication server. It lets you add time-based (TOTP) and event-based (HOTP) one-time passwords to your own applications, VPNs and network devices while keeping every token seed on infrastructure you control.
This image runs the LGPL open-source edition of multiOTP behind nginx, with PHP-FPM 8.3 and OPcache enabled and a self-contained flat-file token store.
What you get:
- The multiOTP application code and its flat-file user, token, device and group store under
/usr/local/bin/multiotp, on a dedicated, independently resizable EBS volume. You can grow your user and token store without touching the operating system disk - No separate database server to install, secure or back up
- The web admin console served over HTTPS on port 443, behind an nginx HTTP Basic authentication gate, so the console is never exposed unauthenticated
- First-boot provisioning on every instance: a fresh administrator password, applied in lock-step to both the nginx Basic-auth gate and the multiOTP web admin; a per-instance self-signed TLS certificate; and the multiOTP default admin password reset to an unusable value
- No shared credentials: two instances launched from the same Amazon Machine Image never share credentials or a TLS key
- The initial administrator password written to
/root/multiotp-credentials.txtwith mode 0600, so that only the root user can read it
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group that allows inbound port 22 from your management network and inbound port 443 from the networks that will reach the web admin
- The AWS CLI (version 2) installed locally, if you plan to deploy from the command line
Launch the instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for multiOTP. Select the SecureAnyCloud listing, choose Select, then choose Continue on the subscription summary.
Pick an instance type of m5.large or larger. Choose your EC2 key pair under Key pair (login). Under Network settings, select your VPC and subnet, and create or select a security group that allows inbound port 22 from your management network and inbound port 443 from the networks that will reach the web admin. Leave the root and data volumes at their default sizes or larger.
Select Launch instance. First-boot initialisation takes approximately one minute after the instance state becomes Running and the status checks pass.
Launch the instance from the AWS CLI
Run the following to launch an instance from the SecureAnyCloud multiOTP Marketplace AMI into an existing subnet and security group.
Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens ports 22 and 443 as described above.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=multiotp-01}]'The command prints a JSON document on success. Note the instance ID. Once the instance is running, retrieve its public address with aws ec2 describe-instances --instance-ids <instance-id> --query "Reservations[].Instances[].PublicIpAddress" --output text.
Connect and retrieve initial credentials
Connect over SSH with the key pair you selected and the public IP address from Step 2. The SSH login user depends on the operating system of the AMI variant you launched:
AMI variant SSH login user
multiOTP 5.10.2.2 on Ubuntu 24.04 ubuntu
For the Ubuntu 24.04 variant, connect with ssh -i <path-to-key.pem> ubuntu@<instance-public-ip>.
First boot stores the generated administrator credentials in a root-only file. Retrieve them with the following command, which prints the multiOTP administrator user name and password for this instance:
sudo cat /root/multiotp-credentials.txtThe file lists the administrator user name admin, the generated password and the web admin URL. Store the password in your password manager and treat the file as sensitive.
Verify the multiOTP service
The web stack is two systemd services: nginx and PHP-FPM. Confirm that both are active:
systemctl is-active nginx php8.3-fpmBoth lines should read active. Confirm that nginx is listening on the HTTPS port 443:
ss -tlnp 2>/dev/null | grep ':443 ' || sudo ss -tlnp | grep ':443 'Confirm that the dedicated data volume holding the application and token store is mounted:
df -h /usr/local/bin/multiotp | tail -1The output shows the data volume mounted at /usr/local/bin/multiotp, for example:
/dev/nvme1n1 9.8G 17M 9.3G 1% /usr/local/bin/multiotpConfirm that the unauthenticated health endpoint is served over HTTPS. The certificate is self-signed, so run curl with -k:
curl -sk -o /dev/null -w 'multiOTP health HTTP %{http_code}\n' https://127.0.0.1/healthAn HTTP 200 response confirms that the web tier is up. Plain HTTP is redirected to HTTPS, and the admin console requires authentication. The first command should report 301 and the second 401:
curl -s -o /dev/null -w 'plain HTTP redirect HTTP %{http_code}\n' http://127.0.0.1/curl -sk -o /dev/null -w 'unauthenticated GET / HTTP %{http_code}\n' https://127.0.0.1/Finally, confirm that the per-instance administrator password is accepted (it returns 200). The command reads the generated password from the root-only credentials file:
PASS=$(sudo grep '^multiotp.admin.pass=' /root/multiotp-credentials.txt | cut -d= -f2-)
curl -sk -o /dev/null -w 'authenticated GET / HTTP %{http_code}\n' -u admin:$PASS https://127.0.0.1/First login to the web admin console
Open a web browser and go to https://<instance-public-ip>/. The image ships a per-instance self-signed certificate, so your browser warns about the certificate the first time. This is expected, and you replace the certificate with a CA-signed one in Step 8. Accept the warning to continue.
The browser first prompts for HTTP Basic authentication. Enter the user name admin and the generated password from /root/multiotp-credentials.txt.
You are then presented with the multiOTP web administration console sign-in form. Sign in with the same user name admin and password.
Once you are signed in, the console shows the multiOTP version and web service status. It gives you the full set of administration actions: change the admin password, import hardware tokens, list hardware tokens, add users, and back up or restore the configuration.
Create users and provision tokens
Use Add a new user in the web console to create an account for each person who will authenticate. Give the user a name, choose a software token, select the token type (for example TOTP), and add the user. You can also create a user and a software TOTP token from the command line:
sudo multiotp -fastcreatenopin aliceTo enrol the user's token on a phone, open the printable provisioning page for that user from the Print button next to the user, or generate it from the command line. The page shows a QR code that you scan with any OATH authenticator app (Google Authenticator, Microsoft Authenticator, FreeOTP and others). It also shows the raw token details (algorithm, Base32 secret seed, number of digits and time interval) for apps that do not support QR provisioning:
sudo multiotp -qrcode alice /tmp/alice-token.pngOnce the token is enrolled, the user generates a rolling one-time password in their authenticator app. Validate a submitted code with multiotp <user> <code>, which exits 0 when the code is accepted. Applications can shell out to it or call the multiOTP web service.
Integrate with applications, VPNs and RADIUS
multiOTP validates one-time passwords in three ways, so you can add MFA to almost anything:
- Command line: your application shells out to
multiotp <user> <otp>and checks the exit status. - Web service: multiOTP answers validation requests over its bundled web service, for applications that prefer an HTTP call.
- RADIUS: front a RADIUS-capable VPN, firewall or network appliance with multiOTP, so that remote access requires a one-time code. multiOTP ships the FreeRADIUS integration scripts under its source tree. Enable and configure the RADIUS server as described in the multiOTP documentation, using a per-deployment RADIUS shared secret. Open the RADIUS ports (UDP 1812/1813) in the instance's security group only to the network devices that need them.
Install a CA-signed certificate with Let's Encrypt
The image serves HTTPS with a per-instance self-signed certificate, so the console is encrypted from first boot. For production use, replace it with a CA-signed certificate so that browsers and clients trust it without a warning.
Point a DNS A record at the instance's public IP address, then install a certificate with Certbot. Replace the example domain with your own domain.
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comFollow the prompts to obtain and install the certificate. Certbot updates the nginx configuration to use the new certificate on port 443.
Keep inbound port 443 open to your client networks. Port 80 is used only for the HTTP-to-HTTPS redirect and the Let's Encrypt challenge.
Back up and maintain
All multiOTP state lives under /usr/local/bin/multiotp on the dedicated data volume: the application, the configuration, and the flat-file user and token store. The config store is under /etc/multiotp/config. Because the data volume is a separate EBS volume, the simplest backup is a point-in-time EBS snapshot:
aws ec2 create-snapshot --volume-id <data-volume-id> --description "multiOTP data backup"To find the data volume ID, describe the instance's block device mappings. You can also back up the configuration from inside the web console with Backup the configuration, which produces an encrypted configuration file.
Apply operating system security updates with sudo apt-get update && sudo apt-get upgrade. The nginx and PHP-FPM services restart cleanly, and multiOTP resumes serving immediately because all state is on the persistent data volume.
Support
This image is published and supported by SecureAnyCloud. For deployment help, token and user provisioning questions, RADIUS integration, certificate setup, upgrade guidance or storage administration, contact SecureAnyCloud support by email or chat.
multiOTP itself is open-source software licensed under the GNU Lesser General Public License version 3 (LGPL-3.0).
Deploy on AWS
Launch multiOTP on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com