Applications Azure

DumbAssets on Ubuntu 24.04 on Azure User Guide

| Product: DumbAssets on Ubuntu 24.04 LTS on Azure

Overview

This image runs DumbAssets 1.0.11, a deliberately simple self hosted asset tracker, on Ubuntu 24.04 LTS. DumbAssets lets you catalogue the physical things you own, electronics, appliances, tools and equipment, together with their manufacturer, model and serial number, purchase date and price, warranties, maintenance reminders, and the photos and receipts that go with them, all from a small web dashboard with search and simple charts.

It keeps its data in plain files on the VM itself with no external database, so your whole catalogue is a set of readable JSON files plus an uploads folder that you fully control.

The image ships a complete, running DumbAssets server, run by an unprivileged dumbassets system account under a systemd service. It runs on Node.js 22 LTS, with nginx as the only front door: the Node process binds 127.0.0.1:3000 and is never directly reachable, and nginx serves the app on port 80.

Security is the point of this image. DumbAssets is protected by a single site PIN, and when that PIN is left blank the application disables authentication entirely and every page is open. This image therefore never ships a default: a fresh VM generates its own random PIN on first boot, the service refuses to start until a real PIN and a unique session secret are in place, and until first boot has run the captured image cannot be signed in to at all.

What is included:

  • DumbAssets 1.0.11 served behind nginx on port 80, with the Node app bound to loopback only
  • Node.js 22 LTS from NodeSource
  • A PIN unique to your VM, generated on first boot; no default or blank PIN ships in the image
  • A session secret unique to your VM, replacing the insecure upstream default
  • File based storage on a persistent data directory at /var/lib/dumbassets/data
  • One clearly labelled demo asset so the dashboard shows real content the first time you sign in
  • dumbassets.service and nginx.service as systemd units, enabled and active
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web interface. DumbAssets serves plain HTTP here; for internet facing use, terminate TLS in front of it with your own domain (a reverse proxy or the Azure Application Gateway) so the PIN and your data are never sent in the clear.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for DumbAssets by cloudimg, and select Create. On Basics 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). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name dumbassets \
  --image <marketplace-image-urn> \
  --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 dumbassets --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

DumbAssets is run by systemd as the unprivileged dumbassets user. nginx is the only front door: it listens on :80 and reverse proxies to the Node process on 127.0.0.1:3000. Confirm the services are active and note that the application port is loopback only:

systemctl is-active dumbassets nginx dumbassets-firstboot
sudo ss -tlnp | grep -E ':80 |:3000 '

Expected output: all three services report active, nginx holds 0.0.0.0:80, and the Node application is bound only to 127.0.0.1:3000.

DumbAssets, nginx and the first boot unit active, with nginx on port 80 and the DumbAssets application listening only on loopback 3000

Step 5 - Retrieve your PIN

On the first boot of every VM, dumbassets-firstboot.service generates a PIN unique to that VM and a unique session secret, and writes the PIN, with the sign in URL, into a root only file. Read it with:

sudo cat /root/dumbassets-credentials.txt

There is no default or shared PIN in the image. DumbAssets treats an empty PIN as "authentication disabled", so the shipped image carries an empty PIN and the service is configured to refuse to start until first boot has written a real one. Store the PIN somewhere safe.

Step 6 - The app is closed to unauthenticated access

The whole application sits behind the PIN. Prove it from the VM: an unauthenticated API request is refused, the root page redirects to the login screen, a wrong PIN is rejected, and only this VM's PIN is accepted. The block reads the PIN from the root only credentials file, so nothing secret is typed on the command line:

PIN=$(sudo grep '^DUMBASSETS_PIN=' /root/dumbassets-credentials.txt | cut -d= -f2-)
curl -s http://127.0.0.1/pin-length; echo
curl -s -o /dev/null -w 'unauthenticated /api/assets -> %{http_code}\n' http://127.0.0.1/api/assets
curl -s -o /dev/null -w 'unauthenticated /           -> %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'wrong PIN                   -> %{http_code}\n' \
  -X POST -H 'Content-Type: application/json' --data '{"pin":"00000000"}' http://127.0.0.1/verify-pin
curl -s -o /dev/null -w 'this VM PIN                 -> %{http_code}\n' \
  -c /tmp/da.jar -X POST -H 'Content-Type: application/json' --data "{\"pin\":\"$PIN\"}" http://127.0.0.1/verify-pin

Expected output: /pin-length reports a non zero length (authentication is on), the unauthenticated API returns 401, the root page returns 302 (a redirect to the login screen), a wrong PIN returns 401, and this VM's PIN returns 302 and sets a session.

Unauthenticated requests refused with 401 and 302, a wrong PIN rejected with 401, this VM PIN accepted, and the root only credentials file shown with 0600 permissions

Step 7 - Your data lives in plain files

DumbAssets stores everything in a persistent directory at /var/lib/dumbassets/data: Assets.json and SubAssets.json hold the catalogue, and Images, Receipts and Manuals hold your uploads. The image ships one clearly labelled demo asset so the dashboard is not empty on your first sign in:

sudo ls -l /var/lib/dumbassets/data
sudo python3 -c 'import json; d=json.load(open("/var/lib/dumbassets/data/Assets.json")); print(len(d), "asset(s):", [a["name"] for a in d])'

Expected output: the data directory listing, and one asset named Example Laptop (demo - safe to delete).

The DumbAssets data directory and the single demonstration asset read straight from Assets.json

Step 8 - The service is hardened

The Node process runs as the unprivileged dumbassets user under systemd with ProtectSystem=strict, and it binds the loopback interface only, so nginx is the sole way in. It also refuses to start unless a real PIN is present, which is what stops the app from ever coming up open:

systemctl show dumbassets.service -p User -p ProtectSystem -p Restart
systemctl is-enabled dumbassets dumbassets-firstboot nginx
sudo ss -tlnp | grep 3000

Expected output: the service runs as dumbassets with ProtectSystem=strict, all three units are enabled, and the application listens only on 127.0.0.1:3000.

The hardened DumbAssets unit running as an unprivileged user with strict filesystem protection, all units enabled, and the application bound to loopback only

Step 9 - Sign in to the dashboard

Browse to http://<vm-public-ip>/. You will be sent to the PIN entry screen. Enter the PIN from Step 5, one digit per box; the form submits itself when the last digit is entered. Until you enter the correct PIN the app gives you nothing.

The DumbAssets PIN sign in screen with one input box per PIN digit

For production, put the app behind your own hostname with TLS termination and restrict :80 to your management network in the NSG, so the PIN and your data are never sent over plain HTTP.

Step 10 - The asset dashboard

Once you are in, you land on the dashboard: your asset list on the left with a search box and an Add Asset button, and an overview on the right with warranty status and a warranties expiring over time chart. The demo asset appears in the list so you can see the layout straight away.

The DumbAssets dashboard showing the demo asset in the list and the warranty analytics overview

Step 11 - Inspect an asset

Select an asset to open its detail view: the manufacturer, model and serial number, purchase date and price, quantity, notes, and its warranty and maintenance information. From here you can edit the asset, attach documents, photos and receipts, and add components (sub assets) that belong to it.

The demo asset detail view showing manufacturer, model number, purchase date and description, with a components section

Step 12 - Add your own assets

Delete the demo asset once you have looked around (open it and use the delete icon), then use Add Asset to start cataloguing your own. Give each asset a name, then fill in as much as you want: manufacturer, model and serial number, purchase date and price, a warranty with an expiry date so DumbAssets can remind you before it lapses, maintenance events, tags, and uploaded photos, receipts and manuals. Use Import Assets to bring in an existing inventory in bulk.

To start completely clean instead, stop the service, clear the data directory and restart:

sudo systemctl stop dumbassets
sudo rm -rf /var/lib/dumbassets/data/*
sudo systemctl start dumbassets

Maintenance

  • Configuration: the runtime environment lives in /etc/dumbassets/dumbassets.env (owned root:dumbassets, mode 0640) and holds the PIN, the session secret, the port and the base URL. Change a value there and run sudo systemctl restart dumbassets.
  • Change the PIN: edit DUMBASSETS_PIN in /etc/dumbassets/dumbassets.env (4 or more digits) and restart the service.
  • Your data: everything is under /var/lib/dumbassets/data. Back that directory up to keep your catalogue, uploads and configuration.
  • Logs: the server logs to the journal. sudo journalctl -u dumbassets -f follows it.
  • Security updates: unattended security upgrades are enabled, so the OS keeps itself patched. Reboot when a new kernel is installed.
  • TLS: for production, front port 80 with your own domain and a TLS terminating reverse proxy or Azure Application Gateway.
  • Rotate the PIN and session secret: remove /var/lib/cloudimg/dumbassets-firstboot.done and /var/lib/dumbassets/.bootstrap-ready, then reboot; first boot regenerates both cleanly.

Support

cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. DumbAssets is licensed under the GNU General Public License v3.0. This image is provided by cloudimg; additional charges apply for build, maintenance and 24/7 support.