Applications AWS

Warracker 1.0.2 on AWS User Guide

| Product: Warracker 1.0.2 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Warracker 1.0.2 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg Helpdesk, ITSM and Project Management listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Warracker is a self hosted warranty tracker: one searchable record of everything you own that came with a warranty, what you paid, when you bought it, when the cover runs out and where the paperwork is. Each product carries its purchase date and price, vendor, serial and model numbers, the warranty length (or an exact expiry date, or lifetime cover), notes and tags, plus the receipt or invoice, the manual, a product photo and any other document you upload. The dashboard colours every warranty by how much cover is left, the status view charts what is active, expiring soon and expired, and Warracker can remind you before cover lapses by email or through Apprise push notifications. It supports several users with an administrator role, optional OpenID Connect single sign on and optional Paperless-ngx integration.

The image runs Warracker natively on Ubuntu 24.04: nginx serves the interface on port 443 and passes the API to gunicorn on the loopback address, backed by PostgreSQL 16 on the same instance.

The cloudimg image adds:

  • HTTPS from the first boot. nginx serves Warracker on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • Nobody can claim your instance. Upstream Warracker makes the first person to register the administrator and owner, and accepts that registration on an empty database whatever the registration setting says. First boot creates exactly one administrator with a password unique to your instance and closes public registration in the same step, before the web server starts.
  • Secrets and data unique to your instance. The administrator password and the key that signs every session token are generated on first boot and written to root only files. The PostgreSQL database itself is created on first boot too: the image ships no database at all, so no account, password or password hash from the build can be in it. Warracker connects to PostgreSQL over the local socket as its own system user, so there is no database password to manage or leak.
  • Fail closed. The application refuses to start with a missing, placeholder or published default signing key, and nginx refuses to start until first boot has finished.
  • Upstream security fixes applied. Warracker 1.0.2 is the newest release. Two security fixes made since on Warracker's own main branch, and not yet released, are applied to it: one stops a signed in user downloading another user's receipts and manuals through the /api/files/ route, the other escapes warranty fields so they cannot inject script into the page. The Python packages are upstream's choices moved to releases with no known vulnerability.
  • Documents stay private. Receipts, manuals and photos are stored readable by the Warracker service only, nginx never serves them from disk, and the only way to download one is through the signed in, owner checked API.
  • Host names from configuration, never from a request. The names Warracker answers to come from your instance's EC2 metadata (or a name you set). Requests for any other host name get no answer, and links in password reset and expiry emails are built from the configured name.
  • Loopback only, metadata blocked. gunicorn (127.0.0.1:5000) and PostgreSQL (127.0.0.1:5432 and the local socket) are reachable only on the instance. The only open ports are 22, 80 and 443. gunicorn, nginx and PostgreSQL are blocked from the instance metadata service, and the AMI requires IMDSv2.
  • The real client address, no cross origin access. nginx passes the address of the actual connection to Warracker, never a header a client can forge, and strips cross origin (CORS) headers so another website cannot use your session.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Warracker 1.0.2 (the GitHub release archive, checksum verified, with the two upstream security fixes), Flask 3.1, gunicorn 23, psycopg2, PostgreSQL 16, nginx, Python 3.12, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH), TCP 443 (Warracker) and TCP 80 (redirects to HTTPS). Allow all three from your own networks only: Warracker holds your purchase records and documents and is meant for you and the people you add.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Warracker 1.0.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=warracker}]'

Step 2: Connect to your instance

Version SSH user
Warracker 1.0.2 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot creates the PostgreSQL database and the Warracker schema; allow about 30 seconds after the instance reaches Running before you sign in. Then the units are active:

systemctl is-active postgresql@16-main warracker nginx warracker-firstboot
active
active
active
active

How long first boot took on your instance:

sudo journalctl -u warracker-firstboot -o cat | grep -E 'migrations took|first boot complete'
[<time>] warracker-firstboot: migrations took <n> s (54 applied)
[<time>] warracker-firstboot: first boot complete in <n> s; Warracker will be served at https://<public-ip>/

gunicorn and PostgreSQL listen on loopback only; nginx serves ports 80 and 443:

sudo ss -tlnp | awk 'NR==1 || /gunicorn|postgres|nginx|sshd/' | awk '{print $1, $4}'
State Local
LISTEN 127.0.0.1:5000
LISTEN 127.0.0.1:5432
LISTEN 0.0.0.0:443
LISTEN 0.0.0.0:80
LISTEN 0.0.0.0:22
LISTEN [::]:443
LISTEN [::]:80
LISTEN [::]:22

Step 4: Get your URL and administrator password

sudo cat /root/warracker-credentials.txt

The file holds the address Warracker is served as (your instance's public IP address by default), the administrator user name and the password generated on this instance:

WARRACKER_URL=https://<public-ip>/
WARRACKER_ADMIN_USERNAME=admin
WARRACKER_ADMIN_EMAIL=admin@example.invalid
WARRACKER_ADMIN_PASSWORD=<WARRACKER_ADMIN_PASSWORD>

The database has no password: the warracker PostgreSQL role can only log in over the local socket as the warracker system user, and no role on the instance has a password at all:

sudo -u postgres psql -Atc "SELECT rolname, rolsuper, rolcreaterole, rolpassword IS NOT NULL FROM pg_authid WHERE rolname IN ('postgres','warracker') ORDER BY 1"
postgres|t|t|f
warracker|f|f|f

The credentials file is readable by root only, and the environment file that holds the session signing key by root and the warracker service only. A checker on the image proves that none of these secrets reached the system journal, any log, /var/backups, shell history or a command line:

sudo stat -c '%a %U:%G %n' /root/warracker-credentials.txt /etc/warracker/warracker.env /etc/ssl/warracker/warracker.key
sudo bash /usr/local/sbin/warracker-secret-leak-check.sh
600 root:root /root/warracker-credentials.txt
640 root:warracker /etc/warracker/warracker.env
600 root:root /etc/ssl/warracker/warracker.key
LEAKCHECK_OK no per-instance secret in the journal, any log, /var/backups, history, MOTD, /etc (outside the env file) or a command line

Step 5: Sign in

Open WARRACKER_URL in your browser. The certificate is self signed (generated on your instance), so accept the browser warning once, or set your own name and certificate (Step 10). Warracker has no anonymous access: you land on the sign in page, which also tells visitors that new account registration is disabled.

The Warracker sign in page served over HTTPS, reporting that new account registration is currently disabled

Sign in as admin with the password from Step 4, then change it under Settings (the user menu at the top right).

The Warracker settings page with the administrator's account details

Step 6: Record a warranty with its paperwork

Select Add New Warranty. The dialog walks through the product, the warranty term, the documents, tags and a summary.

The Add New Warranty dialog with fields for the product name, product URL, serial numbers, model number and vendor

  • Product: name, an optional link to the product page, one or more serial numbers, model number and vendor.
  • Warranty: purchase date and price, then a duration in years, months and days, an exact expiry date, or lifetime cover.
  • Documents: the receipt or invoice, the manual, a product photo and any other supporting file (PDF, PNG, JPG), up to 32 MB each.
  • Tags: labels you can filter on later.

Saved warranties appear on the home page as cards, coloured by how much cover is left (green, orange when expiring soon, red when expired), with a countdown of the days remaining and links to their documents. Search, filter, sort and change the view from the toolbar.

The warranty dashboard with warranty cards showing age, warranty length, expiry date, price, model number, vendor and days remaining

Status charts the whole collection: how many warranties are active, expiring soon and expired, the distribution, an expiration timeline and a table of what has recently lapsed or is about to, which you can export to CSV.

The Warranty Status Dashboard with active, expiring soon, expired and total counts, a distribution chart and an expiration timeline

Step 7: Add people

Public registration is closed, so nobody can create an account on your instance by finding its address. Under Settings, the Admin Settings section lists users and lets you manage them, and its Registration Enabled switch reopens registration for a while if you want family or colleagues to sign themselves up (switch it off again afterwards). Ordinary users see only their own warranties and documents (the Global View Admin Only setting is on); an administrator can change that under Admin Settings.

curl -sk --resolve "$(sudo sed -nE 's#^WARRACKER_URL=https://([^/]+)/.*#\1#p' /root/warracker-credentials.txt):443:127.0.0.1" \
  "https://$(sudo sed -nE 's#^WARRACKER_URL=https://([^/]+)/.*#\1#p' /root/warracker-credentials.txt)/api/auth/registration-status"; echo
{"enabled":false}

Step 8: Your documents are private

Uploaded receipts, manuals and photos live in /var/lib/warracker/uploads, readable by the warracker service only. nginx refuses to serve that directory, and the only download route is the Warracker API, which needs a signed in session and checks that the document belongs to you (or that you are an administrator, or that an administrator has shared that kind of document). The image includes upstream's fix for the older /api/files/ route, which in Warracker 1.0.2 skipped that ownership check. The fixes applied, and the reason for each, are written down on the instance:

sed -n '1,12p' /usr/share/warracker/CLOUDIMG-SECURITY-BACKPORTS.txt
Security fixes applied to Warracker 1.0.2 by cloudimg
=====================================================

Warracker 1.0.2 (2025-10-30) is the newest Warracker release. Two security fixes have since been made on
Warracker's main branch and are not yet in a release; this image carries both, applied to the 1.0.2 source.

1. Upstream commit 696f3a2b (2026-10-04) "Fix authorization bypass on /api/files/ document route"
   In 1.0.2 the legacy /api/files/<path> route served any uploaded document to any signed-in user, with no
   ownership check and no path-traversal guard. Both download routes now share one authorization check
   (the document's owner, an administrator, or the admin's document-sharing setting).
   Applied: backend/file_routes.py (upstream's change, verbatim).

The nginx access log records the path of each request but never its query string, so a session token passed in a link never ends up in the log.

Step 9: Optional expiry email and push notifications

Email and push notifications are optional and switched off by default: cloudimg never bakes a mail account into an image. To send password reset and expiry emails through your own SMTP server, fill in the mail settings in /etc/warracker/warracker.env:

SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=warracker@example.com
SMTP_PASSWORD=<your-smtp-password>
SMTP_SENDER_EMAIL=warracker@example.com
SMTP_FROM_ADDRESS=warracker@example.com
SMTP_USE_TLS=true

then restart the application:

sudo systemctl restart warracker

For push notifications through Apprise (Discord, Slack, Telegram, Teams, ntfy, Pushover, webhooks and many more), sign in as the administrator and enable Apprise under Settings, or set APPRISE_ENABLED=true and APPRISE_URLS in the same file. The reminder schedule (APPRISE_EXPIRATION_DAYS, by default 7 and 30 days before expiry, and APPRISE_NOTIFICATION_TIME) lives there too. Links in emails are always built from the name Warracker is configured with (Step 10), never from the host name a visitor's request claims.

Step 10: Your own DNS name and certificate

Point a DNS record at the instance, then set the name Warracker is served as. This mints a new self signed certificate for the name, updates nginx, the base URL used in email links and single sign on redirects, and the URL in /root/warracker-credentials.txt, and keeps the name across reboots:

sudo warracker-set-hostname <your-domain>

To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running warracker-set-hostname again replaces it with a self signed one):

sudo install -m 0644 <your-domain>.crt /etc/ssl/warracker/warracker.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/warracker/warracker.key
sudo nginx -t && sudo systemctl reload nginx

Step 11: Security checks

A request for a host name the instance does not serve gets no answer, port 80 redirects to HTTPS, the API needs a sign in and nginx refuses to serve uploads from disk:

H=$(sudo sed -nE 's#^WARRACKER_URL=https://([^/]+)/.*#\1#p' /root/warracker-credentials.txt)
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: evil.example' http://127.0.0.1/ || true
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'warranty list without signing in: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/api/warranties"
curl -sk -o /dev/null -w 'uploads straight off disk: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/uploads/anything.pdf"
unknown host name: 000
port 80: 301 -> https://<public-ip>/
warranty list without signing in: 401
uploads straight off disk: 403

The application runs unprivileged in a sandbox, gunicorn, nginx and PostgreSQL cannot reach the instance metadata service, nginx passes the real client address from the connection and strips cross origin headers:

systemctl show warracker -p User -p ProtectSystem -p NoNewPrivileges -p IPAddressDeny
systemctl show nginx postgresql@16-main -p IPAddressDeny
grep -E 'proxy_set_header (X-Forwarded-For|X-Real-IP|X-Forwarded-Host)|proxy_hide_header Access-Control-Allow-Origin' /etc/nginx/sites-available/cloudimg-warracker
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
User=warracker
ProtectSystem=full
NoNewPrivileges=yes
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128

IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Host "";
        proxy_hide_header Access-Control-Allow-Origin;

The certificate was generated on this instance and names its own addresses:

H=$(sudo sed -nE 's#^WARRACKER_URL=https://([^/]+)/.*#\1#p' /root/warracker-credentials.txt)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = <public-ip>, O = cloudimg
X509v3 Subject Alternative Name:
    IP Address:<public-ip>, DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

Step 12: Verify the whole workflow yourself

The image ships the check it is built and tested with. Over HTTPS on the configured name it proves registration is closed and the API refuses anonymous callers, that a wrong administrator password is refused and the real one signs in as the owner, that a session token forged with Warracker's published fallback key is refused, records a warranty with a PDF receipt and downloads it back byte for byte (and is refused without a session), then creates a second, ordinary user and proves that user cannot read the administrator's receipt through either download route, checks the escaping fix is the front end being served, and finally removes the probe warranty and user so nothing is left behind:

sudo bash /usr/local/sbin/warracker-verify-workflow.sh
WORKFLOW_OK warranty=<id> receipt=round-trip anonymous=401 uploads=403 other-user=403/403 forged-jwt=401 register=403

The installed versions:

/opt/warracker/venv/bin/python -c "import importlib.metadata as m; print(' | '.join(f'{p} {m.version(p)}' for p in ('flask','werkzeug','gunicorn','psycopg2','pyjwt','flask-cors')))"
grep -oE "currentVersion = '[^']+'" /var/www/warracker/version-checker.js
head -2 /usr/share/doc/cloudimg/warracker-python-audit.txt | tail -1
flask 3.1.3 | werkzeug 3.1.9 | gunicorn 23.0.0 | psycopg2 2.9.11 | pyjwt 2.15.0 | flask-cors 6.0.5
currentVersion = '1.0.2'
No known vulnerabilities found

Step 13: Backups

Back up the database and the uploaded documents. The dump goes to a root only directory; never copy /etc/warracker/warracker.env into a shared backup location, because it holds the session signing key:

sudo install -d -o root -g root -m 0700 /var/backups/warracker
sudo sh -c 'umask 077; runuser -u postgres -- pg_dump warranty_db | gzip > /var/backups/warracker/warranty_db-$(date -u +%Y%m%d).sql.gz'
sudo tar -C /var/lib/warracker -czf /var/backups/warracker/uploads-$(date -u +%Y%m%d).tar.gz uploads

Copy the files off the instance to your own backup storage, or snapshot the EBS volume.

Step 14: Server components

Component Version Path
Warracker backend 1.0.2 + 2 upstream security fixes /opt/warracker/app, warracker.service, 127.0.0.1:5000
Warracker front end 1.0.2 + upstream escaping fix /var/www/warracker
Python environment Flask 3.1, gunicorn 23 /opt/warracker/venv
PostgreSQL 16 postgresql@16-main.service, 127.0.0.1:5432 and the local socket, /var/lib/postgresql/16/main (created on first boot)
nginx 1.24 /etc/nginx/sites-available/cloudimg-warracker
Settings /etc/warracker/warracker.env (root:warracker, 0640)
Uploaded documents /var/lib/warracker/uploads (warracker, 0750)
Certificate and key /etc/ssl/warracker/
First boot warracker-firstboot.service, /usr/local/sbin/warracker-firstboot.sh
Host name helper /usr/local/sbin/warracker-set-hostname
Credentials /root/warracker-credentials.txt (0600)
Source (AGPL-3.0) /usr/share/warracker/source

Logs: journalctl -u warracker (application), journalctl -u warracker-firstboot (first boot), /var/log/nginx/, /var/log/postgresql/.

Step 15: The complete source is on the instance

Warracker is licensed under the GNU Affero General Public License v3. The exact upstream release archive with its checksum, the licence, the two security fixes as applied and a written offer ship on the image:

cd /usr/share/warracker/source && sha256sum -c warracker-1.0.2-source.tar.gz.sha256 && ls -1
warracker-1.0.2-source.tar.gz: OK
LICENSE
WRITTEN-OFFER.txt
cloudimg-backport-696f3a2b-file-routes-authz.patch
cloudimg-backport-a1ced8b6-xss.py
warracker-1.0.2-source.tar.gz
warracker-1.0.2-source.tar.gz.sha256

Troubleshooting

The site does not answer. First boot may still be running (it creates the database, so give it about 30 seconds), or it failed (then nginx and the application stay stopped by design). Check it:

systemctl show -p ActiveState -p Result warracker-firstboot.service
Result=success
ActiveState=active

If Result is not success, read sudo journalctl -u warracker-firstboot --no-pager.

The browser gets no answer for a name you use. Warracker answers only to its configured name, the instance's own EC2 addresses and names, and localhost. Set your name with sudo warracker-set-hostname <your-domain> (Step 10).

The application will not start after you edited the settings. A guard refuses to start Warracker with an empty or weak signing key or with a database password set. Read why with sudo /usr/local/sbin/warracker-preflight.sh.

The administrator password is lost. With outbound email set up (Step 9), use Forgot Password? on the sign in page. Without email, sign in as another administrator, or ask cloudimg support for the reset procedure.

Security recommendations

  • Allow ports 22, 80 and 443 from your own networks only.
  • Change the admin password after your first sign in, give everyone a named account and keep public registration closed.
  • Keep /root/warracker-credentials.txt and /etc/warracker/warracker.env as shipped. The environment file holds the session signing key; anyone who reads it can forge sessions.
  • Use your own DNS name and a certificate your users trust (Step 10).
  • Keep the operating system patched; unattended security upgrades are on.
  • Back up the database and documents regularly (Step 13).

Support and licensing

Warracker is open source under the GNU Affero General Public License v3 (the source, licence and written offer are in /usr/share/warracker/source); there is no per user or per CPU licence fee. The AMI price covers cloudimg's hardening, first boot setup and support. Warracker is the name of the open source project by sassanix; cloudimg is not affiliated with or endorsed by the Warracker project. Screenshots show demonstration data; the image ships with no warranties.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk
  • Support: 24/7, with a 24 hour response SLA