E-commerce AWS

Open Source Point of Sale 3.4.2 on AWS User Guide

| Product: Open Source Point of Sale 3.4.2 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Open Source Point of Sale 3.4.2 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg e-commerce platforms listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Open Source Point of Sale (OSPOS) is a web based point of sale and inventory management system for shops, cafes and small chains. It runs the till in a browser (barcode scanning, taxes, discounts, cash rounding and several payment types, receipts and invoices) and keeps items, item kits, stock locations, receivings, customers, suppliers, gift cards, expenses and cash ups in one place, with sales, inventory, tax and payment reports.

What the cloudimg image adds:

  • The 3.4.2 security release. OSPOS 3.4.2 fixes the published 3.4.1 vulnerabilities (remote code execution through the invoice and e-mail settings, SQL injection, insecure direct object references, stored cross site scripting, Host header injection) and adds a login throttle: after five failed sign ins in a minute from one address or for one user name, OSPOS answers "Too many attempts" for a short while.
  • Ready to sign in. The schema is fully migrated to 3.4.2 at build time, so the first page you open is the login page, not an installer or a migration prompt.
  • No database baked into the image. The image carries a clean copy of the migrated schema with no credential in it. First boot creates the ospos database and loads that copy (a few seconds: 2 to 5 seconds in our tests), so no database file in the image ever held a password.
  • No database password at all. OSPOS connects to MariaDB as the PHP-FPM user www-data over the local socket, and MariaDB checks the operating system user on the other end of the socket (unix_socket authentication). There is no database password to store, rotate or leak, and the account cannot be used over the network or by any other user.
  • No default login. Upstream OSPOS seeds the well known admin / pointofsale login. On this image that password is replaced at build time and never ships; first boot gives the admin a password unique to your instance.
  • Secrets unique to your instance. The OSPOS admin password, the application encryption key and the login throttle key are generated on first boot and written to /root/opensourcepos-credentials.txt (root only). The image ships with no password or key in the configuration, and nginx and PHP-FPM refuse to start until first boot has finished.
  • HTTPS from the first boot. nginx serves OSPOS on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, requests for a host name the instance does not serve are dropped, and the session cookie is Secure and HttpOnly.
  • Only the application on the web. nginx serves the public/ directory only and runs exactly one PHP file, the application's front controller. Any other .php file, including anything uploaded, answers 404, and uploads are served as images only. OSPOS's own cross site request forgery filter does not cover the login page, so nginx also refuses form posts that come from another web site.
  • Hardened defaults. MariaDB listens on 127.0.0.1 only. nginx, PHP-FPM, MariaDB and the first boot service are blocked from the instance metadata service, the AMI requires IMDSv2, and PHP is handed the configured host name and the real client address from the connection (never values a client sends).
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Open Source Point of Sale 3.4.2 (the official release opensourcepos.3.4.2.cb5e2a.zip, checksum verified), nginx, PHP 8.3 FPM with OPcache, MariaDB, 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) from your admin network, and TCP 443 (OSPOS) and TCP 80 (redirects to HTTPS) from the networks your tills and staff work from. Never open 3306.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Open Source Point of Sale 3.4.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. The database lives on the root volume, so for production give the root volume room to grow (20 GiB or more).

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> \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=20,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ospos}]'

First boot takes well under a minute after the instance is running; creating and loading the database takes a few seconds of it.

Step 2: Connect to your instance

Version SSH user
Open Source Point of Sale 3.4.2 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot creates the database, sets the passwords, the keys and the certificate, then nginx and PHP-FPM start:

systemctl is-active nginx php8.3-fpm mariadb opensourcepos-firstboot
sudo test -f /var/lib/cloudimg/opensourcepos-firstboot.done && echo FIRSTBOOT_DONE

All four units are active and the first boot marker is present:

active
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on network ports; MariaDB listens on the loopback address and PHP-FPM uses a local socket:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sort
0.0.0.0:22 users:(("sshd",...))
0.0.0.0:443 users:(("nginx",...))
0.0.0.0:80 users:(("nginx",...))
127.0.0.1:3306 users:(("mariadbd",...))
[::]:22 users:(("sshd",...))
[::]:443 users:(("nginx",...))
[::]:80 users:(("nginx",...))

Step 4: Get your URL and passwords

sudo cat /root/opensourcepos-credentials.txt

The file is readable by root only:

# Open Source Point of Sale 3.4.2 - generated on first boot by opensourcepos-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.

OSPOS_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
OSPOS_ADMIN_USER=admin
OSPOS_ADMIN_PASSWORD=<OSPOS_ADMIN_PASSWORD>

MARIADB_DATABASE=ospos
# OSPOS connects as the PHP-FPM user over the local socket; MariaDB authenticates it by the socket (unix_socket),
# so there is no database password to keep. On-box maintenance: sudo mariadb
MARIADB_USER=www-data

# The application encryption key (also in /var/www/opensourcepos/.env); OSPOS encrypts stored integration secrets with it.
OSPOS_ENCRYPTION_KEY=<OSPOS_ENCRYPTION_KEY>
# The HMAC key OSPOS uses for its login-throttle counters (also in .env); nothing else depends on it.
OSPOS_THROTTLE_KEY=<OSPOS_THROTTLE_KEY>

# The certificate is self signed (minted on this instance); port 80 redirects to https.
# Change the name OSPOS is served as:  sudo opensourcepos-set-hostname <your-dns-name>
# MariaDB listens on 127.0.0.1 only; on-box maintenance: sudo mariadb
# User guide: https://www.cloudimg.co.uk/guides/open-source-point-of-sale-3-4-2-on-ubuntu-24-04-aws/

The URL uses the instance's public DNS name from the EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too, although OSPOS sends you to the configured name after you sign in. If you stop and start the instance without an Elastic IP, the boot service picks up the new address and issues a new certificate for it. To serve OSPOS under your own DNS name, see Step 8.

There is no MariaDB password: OSPOS reaches the database as www-data over the local socket, and you reach it as root with sudo mariadb. Keep the encryption key with your backups: OSPOS uses it for the integration secrets you save (for example the SMTP password and the Mailchimp API key). The throttle key only protects the login throttle counters.

Step 5: Sign in

Open the OSPOS_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (or install your own, Step 8). Sign in as admin with the password from Step 4:

The OSPOS login page with the Username and Password fields, the Go button and the Open Source Point of Sale footer

The home page has a tile for each module. The footer shows the version and the link to the project, which the OSPOS licence requires to stay on every page:

The OSPOS home page with the Customers, Items, Item Kits, Suppliers, Reports, Receivings, Sales, Gift Cards, Messages, Expenses, Cashups and Office modules and the opensourcepos.org 3.4.2 footer

You can also check the sign in from the instance. This script signs in through the real login form over HTTPS with the instance's password, then shows that a blank password, the upstream default pointofsale and common weak passwords are refused with OSPOS's "Invalid username and/or password" error. It waits out the login throttle between attempts, so it takes about half a minute:

sudo opensourcepos-cred-roundtrip.sh
OK per-instance admin signs in over HTTPS; blank, admin/pointofsale and weak guesses refused

Step 6: Set up your store

The image ships the upstream starting data: one employee, admin, whose person record is the placeholder John Doe, and the store details Open Source Point of Sale, 123 Nowhere street. There are no items, customers, suppliers or sales. Work through these first:

  1. Office, then Configuration, Information tab: your company name, address, phone, e-mail and logo.
  2. The General, Tax and Localization tabs: default tax rates, currency symbol, number and date formats and the time zone. Then Receipt, Invoice and Barcode for printed output.

The Configuration Information tab with the Company Name, Company Logo, Address, Website, Email and Phone fields and the General, Tax, Localization, Barcode, Stock, Receipt, Invoice, Shortcuts, Reward, Table and Setup & Conf tabs

  1. Office, then Employees: edit the admin employee's name and e-mail, and add an employee for each person with only the modules they need.
  2. Items: add items one at a time with New Item, or load them from a spreadsheet with CSV Import. Item Kits bundle several items under one price.

The Items module with New Item, CSV Import, Bulk Edit and Generate Barcodes and an empty item table

  1. Sales is the register: scan or search for items, choose a customer if needed, take payment and print the receipt.

The Sales register with Register Mode, Find or Scan Item or Receipt, New Item, the cart, Select Customer and the total

Reports covers sales, items, customers, employees, payments, taxes, discounts and inventory, as graphs, summaries or detail:

The Reports module with the Graphical, Summary, Detailed and Inventory report lists

Step 7: E-mail (optional)

OSPOS can e-mail receipts and invoices. Set your SMTP relay under Office, Configuration, Setup & Conf, Email. Amazon SES works as the relay (port 587 with STARTTLS); allow outbound 587 in your security group or network ACLs if you restrict egress.

Step 8: Your own DNS name and certificate

Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name OSPOS is served as. This mints a new self signed certificate for the name, updates nginx, the host name OSPOS accepts (app.allowedHostnames in /var/www/opensourcepos/.env) and the URL in /root/opensourcepos-credentials.txt, and keeps the name across reboots:

sudo opensourcepos-set-hostname <your-domain>

To use a certificate from a public or internal certificate authority instead (for example one issued by Let's Encrypt for <your-domain>), copy it over the generated pair and reload nginx (running opensourcepos-set-hostname again replaces it with a self signed one):

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

Step 9: Security checks

nginx serves the application and its static files only. The configuration file, the application and vendor code and any PHP file other than the front controller answer 404, the licence stays readable, and plain HTTP redirects to HTTPS:

H=$(sudo grep '^OSPOS_URL=' /root/opensourcepos-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
for p in /login /.env /app/Config/App.php /vendor/autoload.php /uploads/item_pics/test.php /license/LICENSE; do
  curl -sk -o /dev/null -w "%{http_code}  $p\n" --resolve "$H:443:127.0.0.1" "https://$H$p"
done
curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
[ "$(curl -sk -o /dev/null -w '%{http_code}' --resolve "$H:443:127.0.0.1" "https://$H/.env")" = "404" ]
200  /login
404  /.env
404  /app/Config/App.php
404  /vendor/autoload.php
404  /uploads/item_pics/test.php
200  /license/LICENSE
http:// -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/

A form post from another web site is refused, and a request for a host name the instance does not serve gets no answer (curl reports 000):

H=$(sudo grep '^OSPOS_URL=' /root/opensourcepos-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
curl -sk -o /dev/null -w 'cross-site post -> %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' --data 'username=admin&password=x' "https://$H/login"
curl -sk -o /dev/null -w 'unknown host -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" https://evil.example/ || true
cross-site post -> 403
unknown host -> 000

OSPOS reports version 3.4.2 and every database migration has run:

grep "application_version =" /var/www/opensourcepos/app/Config/App.php
sudo mariadb -N -B ospos -e "SELECT COUNT(*) FROM ospos_migrations;"
    public string $application_version = '3.4.2';
45

OSPOS's database account is the PHP-FPM user over the local socket, with no password; the same account is refused over TCP:

sudo mariadb -N -B -e "SELECT CONCAT(User,'@',Host,' ',JSON_VALUE(Priv,'$.plugin')) FROM mysql.global_priv WHERE User='www-data';"
sudo -u www-data mariadb -N -B -e "SELECT 'socket login ok'" ospos
sudo -u www-data mariadb --skip-ssl --protocol=tcp -h 127.0.0.1 -N -B -e "SELECT 1" ospos 2>&1 || true
www-data@localhost unix_socket
socket login ok
ERROR 1130 (HY000): Host '127.0.0.1' is not allowed to connect to this MariaDB server

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

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

None of the services can reach the instance metadata service:

systemctl show nginx php8.3-fpm mariadb opensourcepos-firstboot -p Id -p IPAddressDeny
IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=nginx.service

IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=php8.3-fpm.service

IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=mariadb.service

IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
Id=opensourcepos-firstboot.service

nginx hands PHP the configured host name and the client address from the connection, never values a client sends, and OSPOS accepts only the configured host name:

grep -E 'fastcgi_param (HTTP_HOST|HTTPS|HTTP_X_FORWARDED_FOR|HTTP_X_REAL_IP|HTTP_CLIENT_IP) ' /etc/nginx/sites-available/cloudimg-opensourcepos
sudo grep -E '^(CI_ENVIRONMENT|app\.allowedHostnames|app\.forceGlobalSecureRequests|cookie\.secure) ' /var/www/opensourcepos/.env
        fastcgi_param HTTP_HOST $ospos_host;
        fastcgi_param HTTPS on;
        fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
        fastcgi_param HTTP_X_REAL_IP $remote_addr;
        fastcgi_param HTTP_CLIENT_IP $remote_addr;
CI_ENVIRONMENT = production
app.forceGlobalSecureRequests = true
app.allowedHostnames = 'ec2-<public-ip-dashed>.compute-1.amazonaws.com'
cookie.secure = true

Step 10: Changing passwords

  • OSPOS admin password: sign in as admin, open Office, Employees, edit the admin employee, open the Login tab, enter a new password twice and submit. Then update or delete OSPOS_ADMIN_PASSWORD in /root/opensourcepos-credentials.txt so it does not hold an old password.
  • Give each person their own employee login with only the modules they need, rather than sharing admin.
  • MariaDB: there is no database password to change. The www-data account only works over the local socket for the PHP-FPM user; keep it that way rather than adding a password-based account.
  • Encryption key: do not change encryption.key in .env once you have saved integration secrets; OSPOS could no longer read them.
  • Login throttle: five failed sign ins per minute per address or per user name are allowed; set throttle.capacity and throttle.seconds in .env to tune it (throttle.capacity = 0 switches it off, which we do not recommend).

Step 11: Backup and maintenance

The store database lives in MariaDB (/var/lib/mysql), item pictures and the company logo live under /var/www/opensourcepos/public/uploads, and the configuration (with the encryption key) is /var/www/opensourcepos/.env. The simplest backup is an EBS snapshot of the root volume. For a consistent file level copy while OSPOS is running:

sudo mariadb-dump --single-transaction --routines --triggers ospos | gzip > <backup-dir>/ospos-$(date +%F).sql.gz
sudo tar -czf <backup-dir>/ospos-files-$(date +%F).tar.gz -C /var/www/opensourcepos .env public/uploads

Operating system security updates install automatically; run sudo apt-get update && sudo apt-get upgrade to apply everything else. nginx, PHP-FPM and MariaDB restart cleanly and OSPOS keeps all of its data.

Component Path
OSPOS /var/www/opensourcepos (web root public/)
Configuration /var/www/opensourcepos/.env (mode 0640)
Item pictures and logo /var/www/opensourcepos/public/uploads
Sessions, logs and cache /var/www/opensourcepos/writable
Database MariaDB database ospos (/var/lib/mysql)
nginx site /etc/nginx/sites-available/cloudimg-opensourcepos
Certificate and key /etc/ssl/opensourcepos/
Boot service /usr/local/sbin/opensourcepos-firstboot.sh
Host name helper /usr/local/sbin/opensourcepos-set-hostname
Credentials /root/opensourcepos-credentials.txt (mode 0600)

Step 12: Security recommendations

  • Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your tills and staff work from. Never open 3306.
  • Use your own DNS name and a certificate your browsers trust (Step 8).
  • Give each person their own employee login, and change the admin password if it may have been shared (Step 10).
  • Keep the operating system patched, and snapshot the root volume regularly.

Support and licensing

Open Source Point of Sale is open source under the MIT licence, with the project's additional condition that the footer signature (version, commit and the opensourcepos.org link) stays visible and unmodified on every page; this image ships it unchanged. There is no per user or per till licence fee. The AMI price covers cloudimg's hardening, first boot setup and support.

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