Security Azure

Onetime Secret on Ubuntu 24.04 on Azure User Guide

| Product: Onetime Secret on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Onetime Secret on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Onetime Secret is an open source, self hosted service for sharing sensitive information safely. Instead of pasting a password into email or chat where it lingers, you paste it into Onetime Secret and get back a one time link; the recipient opens the link once to reveal the secret, after which it is permanently burned and the link stops working. Secrets also carry a time to live, so anything left unread expires on its own.

The image builds the latest stable Onetime Secret release reproducibly from source at build time (the exact version and source commit are recorded in /var/lib/onetimesecret/VERSION). Onetime Secret is a Ruby/Rack application served by Puma and runs under systemd as the unprivileged onetime system user, bound to loopback only (127.0.0.1:3000). Secrets are stored in Redis, also bound to loopback (127.0.0.1:6379) and protected by a per instance password. nginx is the sole network facing surface and terminates TLS on port 443, reverse proxying to Puma.

The image is secure by default with no known credential baked in. Every per instance secret is generated on first boot only: the application root secret (from which all other cryptographic material is derived), the Redis password, and the colonel (administrator) account password. The colonel credentials and the per VM connection details are written to a root only file at /root/onetimesecret-info.txt. The core product — sharing a one time secret — needs no login at all; the colonel account is the optional administrator.

What is included:

  • The latest stable Onetime Secret release, built from source and run under systemd as the unprivileged onetime system user

  • Puma bound to loopback only (127.0.0.1:3000) and Redis bound to loopback (127.0.0.1:6379) with a per instance password — neither is network exposed

  • nginx terminating TLS on :443 and reverse proxying to the application, with HSTS and sensible security headers

  • Port :80 returns a 301 redirect to HTTPS for every path except an unauthenticated /healthz endpoint (HTTP 200) for load balancer probes

  • A self signed TLS certificate regenerated per VM on first boot (SAN includes the VM public IP + hostname) — replace it with your own CA signed certificate for production

  • A colonel administrator account created on first boot with a unique random password, plus a per instance API token, both written to /root/onetimesecret-info.txt

  • A built in one time self test at /opt/ots/onetimesecret-selftest.sh that proves the self destruct semantics and the colonel credential end to end over TLS

  • Ubuntu 24.04 LTS base with latest security patches applied at build time

  • Azure Linux Agent for seamless cloud integration and SSH key injection

  • 24/7 cloudimg support with guaranteed 24 hour response SLA

Prerequisites

  • An Azure subscription with permission to deploy virtual machines

  • An SSH key pair for administrative access to the VM as the azureuser account

  • A Network Security Group allowing inbound TCP 443 (HTTPS) and 80 (redirect) from the networks that should reach the service, and 22 (SSH) from your management network only

  • A recommended size of Standard_B2s or larger

Step 1: Deploy from the Azure Portal

  1. Locate the Onetime Secret on Ubuntu 24.04 LTS by cloudimg image in the Azure Marketplace and select Create.

  2. Choose your subscription, resource group and region.

  3. Select a VM size (Standard_B2s or larger) and provide your SSH public key for the azureuser account.

  4. On the Networking tab, allow inbound 443 and 80 from your users, and restrict 22 to your management network.

  5. Review and create. When the VM is running, browse to https://<your-public-ip>/.

Step 2: Deploy from the Azure CLI

You can deploy the same image from the Azure CLI. Replace the resource group, location and image URN as needed:

az vm create \
  --resource-group my-onetimesecret-rg \
  --name onetimesecret \
  --image <cloudimg-onetimesecret-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard
# then open the required ports
az vm open-port --resource-group my-onetimesecret-rg --name onetimesecret --port 443 --priority 1001
az vm open-port --resource-group my-onetimesecret-rg --name onetimesecret --port 80 --priority 1002

Step 3: Verify the Onetime Secret, Redis and nginx services

SSH in as azureuser and confirm the three services are active, and that Puma and Redis are bound to loopback only with nginx the only public listener:

sudo systemctl is-active redis-server onetimesecret-web nginx
ss -tln | grep -E ':3000 |:6379 |:80 |:443 '

You should see onetimesecret-web (Puma) on 127.0.0.1:3000 and Redis on 127.0.0.1:6379 (both loopback only), while nginx listens on 0.0.0.0:80 and 0.0.0.0:443.

systemctl reports redis-server, onetimesecret-web and nginx all active; ss shows Puma bound to 127.0.0.1:3000 and Redis bound to 127.0.0.1:6379 loopback only, with nginx public on :80 and :443

Step 4: Open the Onetime Secret web interface

Browse to https://<your-public-ip>/. Because the certificate is self signed and generated per VM, your browser will warn about the certificate the first time. Accept it (or install your own certificate, see Step 12). You are presented with the create a secret form: paste a password, secret message or private link, optionally set a passphrase, and choose how long the secret should live before it expires on its own.

The Onetime Secret home page with a secret typed into the create form, a passphrase field, an expiry of Expires in 7 days selected, and a reminder that the message will self-destruct after being viewed and the link can only be accessed once

Step 5: Create the secret and get a one time link

Select Create Link. Onetime Secret stores the secret and shows a one time link you can share with the recipient, along with a reminder to share the link separately from any other details. A timeline shows when the secret was created and when it will expire.

The Secret created successfully screen showing the generated one time secret link, an Encrypted badge, a reminder to share the link separately, and a timeline of the secret's creation and expiry

Step 6: Reveal the secret once

When the recipient opens the link they are warned that the secret will be shown only once. After they choose to reveal it, Onetime Secret displays the secret with a copy button. This is the single permitted view.

The recipient view after clicking reveal, showing the decrypted secret message in a read only field with a Copy to clipboard button and a note that the window can be closed when done

Step 7: Confirm the secret self destructs

Opening the same link a second time proves the one time guarantee: the secret has already been consumed and permanently deleted, so Onetime Secret reports that it has been viewed or has expired.

Opening the same one time link a second time shows a This secret has been viewed or expired page, confirming the secret self destructed after its single view

Step 8: Verify the one time semantics from the command line

The image ships a self test that proves the self destruct behaviour end to end over TLS. It creates a one time secret through the API, reads it once (HTTP 200), reads it again (HTTP 404), and then confirms the per instance colonel credential authenticates against the API (200 with the real token, 401 with a wrong one):

sudo /opt/ots/onetimesecret-selftest.sh

The expected output confirms the round trip and the credential check: firstReveal=200 secondReveal=404 and colonel API auth=200 wrongToken=401.

The onetimesecret-selftest.sh output reporting round-trip OK with the first reveal returning 200 and the second returning 404, and the colonel API authentication returning 200 with the valid token and 401 with a wrong token

Step 9: Confirm TLS and inspect the per instance certificate

Confirm that plain HTTP redirects to HTTPS, that the web UI and API answer over TLS, and inspect the per VM self signed certificate:

curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' http://127.0.0.1/
curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1/
sudo openssl x509 -in /etc/nginx/tls/onetimesecret.crt -noout -subject -ext subjectAltName

curl shows plain HTTP 80 returning a 301 redirect to https, the web UI and API returning HTTP 200 over TLS, and openssl printing the per VM self signed certificate subject and Subject Alternative Names

Step 10: Sign in as the colonel administrator

Onetime Secret creates a colonel (administrator) account on first boot with a unique random password. The email, password and a per instance API token are written to a root only file. View the baked version and the per VM appliance details with:

cat /var/lib/onetimesecret/VERSION
sudo cat /root/onetimesecret-info.txt

The /var/lib/onetimesecret/VERSION file showing the baked Onetime Secret version, tag and build time, and the per VM onetimesecret-info.txt showing the public URL and colonel email with the password, API token and Redis password redacted

To sign in as the administrator, browse to https://<your-public-ip>/signin and enter the colonel email and the password from /root/onetimesecret-info.txt.

The Onetime Secret sign-in page with the Email and Password fields and a Sign In button, used to log in as the colonel administrator with the per instance credentials from the root only info file

Step 11: How the appliance is wired

  • Onetime Secret application — a Ruby/Rack app served by Puma, run by onetimesecret-web.service as the unprivileged onetime user from /var/lib/onetimesecret, bound to 127.0.0.1:3000, serving both the API and the Vue web frontend.

  • Redisredis-server.service, bound to 127.0.0.1:6379 and protected by a per instance password, holding the secrets with their time to live. Onetime Secret requires Redis, so onetimesecret-web.service declares a dependency on it.

  • nginxnginx.service, terminating TLS on :443 and reverse proxying to the application, redirecting :80 to HTTPS, and serving an unauthenticated /healthz for load balancer probes.

  • First bootonetimesecret-firstboot.service runs once on first boot: it rotates the Redis password, generates the application root secret, writes the per VM configuration, regenerates the per VM self signed TLS certificate, creates the colonel administrator, proves the one time round trip, and writes /root/onetimesecret-info.txt.

Step 12: Use your own domain and a trusted certificate (production)

The image ships a self signed certificate so it is secure out of the box, but browsers will warn on it. For production, point a DNS name at the VM and install a CA signed certificate. With your own domain you can obtain a free certificate from Let's Encrypt:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

Certbot installs the certificate into nginx and sets up automatic renewal. When you move to your own domain, set the public hostname Onetime Secret uses for its links by editing HOST in /var/lib/onetimesecret/.env (for example HOST=secrets.example.com), then run sudo systemctl restart onetimesecret-web.

Step 13: Managing the Onetime Secret service

sudo systemctl restart onetimesecret-web
sudo journalctl -u onetimesecret-web.service --no-pager | tail -20

Application settings are read from /var/lib/onetimesecret/.env (the root secret, the Redis URL, the public HOST and the colonel email). After changing that file, run sudo systemctl restart onetimesecret-web to apply it.

Step 14: Security recommendations

  • Restrict SSH. Allow inbound 22 only from your management network; keep 443 (and 80 for the redirect) open to your users.

  • Install a trusted certificate. Replace the self signed certificate with a CA signed one (Step 12) so recipients are not trained to click through warnings.

  • Keep the colonel credentials safe. They live in /root/onetimesecret-info.txt (root only). Sign in once and change the password if you prefer, and treat the API token as a secret.

  • Keep secrets short lived. The shortest expiry that still works for your recipients gives the smallest window of exposure. Every secret is one time by design.

  • Keep the image updated. Unattended security upgrades remain enabled so the OS keeps receiving patches.

Step 15: Support and Licensing

Onetime Secret is open source software distributed under the MIT License. This image bundles the upstream Onetime Secret release built from source; the licence file is retained on the image under /opt/ots/LICENSE.txt. cloudimg packaging, hardening and support are provided by cloudimg.

Deploy on Azure

Find this image on the Azure Marketplace and deploy it in minutes. See www.cloudimg.co.uk for the full catalogue.

Need Help?

cloudimg provides 24/7 support with a guaranteed 24 hour response SLA. Contact us through www.cloudimg.co.uk for deployment assistance, configuration help or any questions about this image.