Developer Tools AWS

SpiffArena 4.0.0 on AWS User Guide

| Product: SpiffArena 4.0.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the SpiffArena 4.0.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg developer and API tools listing on AWS Marketplace; you pick it as the software version when you launch.

SpiffArena is an open-source workflow platform built on the SpiffWorkflow engine. You draw a business process as a BPMN diagram (or a decision table as DMN) in the browser, add forms, script tasks and service calls, and run it: SpiffArena keeps every process instance, the tasks waiting for people, their data and the full history.

What cloudimg adds on top of SpiffArena:

  • HTTPS from the first boot. nginx serves SpiffArena on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS, and a request for any name other than your instance's own gets no answer.
  • No demo logins. SpiffArena's built-in sign-in ships with demonstration users and fixed passwords. On this image none of them exist: first boot creates one administrator with a random name (spiffadmin-<random>) and a random password, and writes both to /root/spiffarena-credentials.txt.
  • A login gate in front of the built-in OpenID provider. Upstream's built-in provider is meant for demonstrations: its token endpoint signs in whoever presents a sign-in code. On this image a small gate on the loopback address checks the password and binds the sign-in to it, and nginx refuses the provider's token and refresh endpoints, the metrics and the webhooks from the network.
  • Per-instance secrets. The Flask session key, the encryption key, the OpenID client secret, the OpenID signing key, the gate key, the administrator password and the TLS key are all generated on your instance's first boot. The image ships with an empty data directory.
  • Script tasks cannot reach metadata or your network. SpiffArena runs the Python in script tasks on the server. On this image the SpiffArena account is fenced: it can reach public HTTPS but not the EC2 instance metadata service, link-local, private, shared, loopback or multicast addresses.
  • A hardened front door. Sign-in requests are rate limited per client address for every spelling of the sign-in paths, nginx always replaces X-Forwarded-For, cross-site requests are refused, and request sizes and types are limited.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: SpiffArena 4.0.0 (/opt/spiffarena, the upstream v4.0.0 release with SpiffWorkflow pinned to its release commit and its Python dependencies raised to fixed releases), its React web interface built from the same release, SQLite, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. The build record, SpiffArena's licences and the source reference are in /usr/share/doc/cloudimg-spiffarena.

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.
  • A security group that allows TCP 443 (SpiffArena), TCP 80 (redirects to HTTPS) and TCP 22 (SSH). Open 443 to the networks your users work from; keep 22 open to your own networks only.
  • A current web browser.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick SpiffArena 4.0.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose the instance type, key pair, subnet and the security group 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=spiffarena}]'

An Elastic IP keeps the server's address, and so its name and certificate, the same across stops and starts.

Step 2: Connect to your instance

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

Step 3: Check the services

First boot takes about a minute: it sets the server's name, generates the certificate, lays down SpiffArena's database and example process model, creates the administrator and mints every key, and only then starts SpiffArena and nginx. Check that everything is running:

systemctl is-active spiffarena-egress-fence spiffarena-authgate spiffarena spiffarena-scheduler nginx \
  && sudo test -f /var/lib/cloudimg/spiffarena-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
active
active
FIRSTBOOT_DONE

spiffarena is the API (gunicorn), spiffarena-scheduler runs timers and background work, spiffarena-authgate is the login gate and spiffarena-egress-fence loads the outbound fence. Only SSH and nginx listen on the network; the API and the gate listen on the loopback address only:

sudo ss -lntp | awk 'NR>1 && $4 !~ /^127\.0\.0\.5[34]/ {print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u | tee /dev/stderr \
  | awk '$1 !~ /^127\./ {sub(/.*:/, "", $1); print $1}' | sort -un | xargs | grep -qx '22 80 443' && echo "network listeners: 22 80 443"
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:7000 users:(("gunicorn"),("gunicorn"),("gunicorn"))
127.0.0.1:7001 users:(("python3"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
network listeners: 22 80 443

Step 4: Get your address and sign-in

Run sudo cat /root/spiffarena-credentials.txt to see your address, user name and password. The file is readable by root only and holds these settings, all generated for this instance:

sudo sed -n 's/^\(SPIFFARENA_[A-Z0-9_]*\)=.*/\1/p' /root/spiffarena-credentials.txt | tee /dev/stderr \
  | grep -qx SPIFFARENA_PASSWORD && echo "credentials present"
SPIFFARENA_URL
SPIFFARENA_HOST
SPIFFARENA_USERNAME
SPIFFARENA_PASSWORD
SPIFFARENA_TLS_CERT
SPIFFARENA_TLS_SHA256_FINGERPRINT
credentials present

SPIFFARENA_URL is https://<your instance's public DNS name>/, the address you open in a browser, SPIFFARENA_USERNAME is the administrator's user name and SPIFFARENA_PASSWORD its password. The settings that are not secret can be shown on their own:

sudo grep -E '^SPIFFARENA_(URL|HOST|USERNAME)=' /root/spiffarena-credentials.txt \
  | tee /dev/stderr | grep -q '^SPIFFARENA_USERNAME=spiffadmin-' && echo "per-instance administrator"
SPIFFARENA_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
SPIFFARENA_HOST=ec2-203-0-113-10.compute-1.amazonaws.com
SPIFFARENA_USERNAME=spiffadmin-3f9a1c
per-instance administrator

The database holds no user but your administrator (SpiffArena adds a user's row at that user's first sign-in); the upstream demo users are not there:

U="$(sudo sed -n 's/^SPIFFARENA_USERNAME=//p' /root/spiffarena-credentials.txt)"
sudo -u spiffarena sqlite3 -readonly /var/lib/spiffarena/db.sqlite3 "SELECT 'other users: ' || count(*) FROM user WHERE username <> '$U'" \
  | tee /dev/stderr | grep -qx 'other users: 0'
other users: 0

Step 5: Sign in

The certificate is self-signed and generated on your instance, so your browser warns about it the first time. Compare the fingerprint the browser shows with SPIFFARENA_TLS_SHA256_FINGERPRINT and accept it, or use your own domain and certificate (Step 10).

Open SPIFFARENA_URL in your browser. SpiffArena sends you to its sign-in form; sign in with SPIFFARENA_USERNAME and SPIFFARENA_PASSWORD. The form's note about demonstration use is upstream's text: on this image the form checks your per-instance password through the login gate.

SpiffArena sign-in form

After signing in you land on the home page, which lists the tasks waiting for you and the workflows you started:

SpiffArena home page

Change the password with sudo spiffarena-rotate-password (Step 9). The built-in sign-in reads its users from /etc/spiffarena/permissions.yml (readable by root and the spiffarena account only). To add people, give each their own entry under users: with a long random password, add them to a group under groups:, and run sudo systemctl restart spiffarena; for more than a few people, connect SpiffArena to your own OpenID provider instead, as SpiffArena's documentation describes. See Step 12 for who should be allowed to edit process models.

Step 6: Build and run a process

Processes lists process groups and the process models inside them. The image ships one example, cloudimg Examples > Hello World: a start event, a script task that sets some process data, and an end event.

SpiffArena process groups

Open a model's BPMN file to edit the diagram in the browser. The palette on the left adds events, tasks, gateways and data objects; the panel on the right sets each element's properties, such as a script task's Python, a user task's form or a service task's connector:

SpiffArena BPMN editor

Press Start to run the model. SpiffArena runs the instance, and its page shows the status, who started it, the milestones, the events and the task data:

A completed process instance

Process models are stored as files in /var/lib/spiffarena/process-models, a git repository that SpiffArena commits to on every save, so each instance records the revision it ran. Add Process Group and Add a process model create your own; SpiffArena's documentation covers forms, user tasks, service tasks, messages and DMN decision tables.

Step 7: Script tasks and the egress fence

Script tasks run Python on the server, inside SpiffArena's API process, as the spiffarena account. On a cloud instance a script that can open network connections is a server-side request forgery risk, so the spiffarena account is fenced at two layers:

  • nftables (spiffarena-egress-fence.service) rejects any connection the spiffarena user opens to metadata, link-local, private (RFC 1918), shared, loopback (except SpiffArena's own API port), multicast or reserved addresses, IPv4 and IPv6.
  • systemd (IPAddressDeny= on spiffarena.service and spiffarena-scheduler.service) drops the same ranges, except loopback, for anything running in those services, whatever user it runs as.

The SpiffArena services refuse to start unless the nftables table is loaded, and nginx cannot reach the metadata service either. Check the fence:

sudo spiffarena-egress-fence check && echo "egress fence loaded"
systemctl show -p IPAddressDeny --value spiffarena.service | tr ' ' '\n' | grep -cE '^(10\.0\.0\.0/8|172\.16\.0\.0/12|192\.168\.0\.0/16|169\.254\.0\.0/16)$' \
  | tee /dev/stderr | grep -qx 4 && echo "private and link-local ranges denied to spiffarena.service"
egress fence loaded
4
private and link-local ranges denied to spiffarena.service

The spiffarena user is refused at once by the metadata service and by this instance's own private address, while public HTTPS works:

P="$(hostname -I | awk '{print $1}')"
for u in http://169.254.169.254/latest/meta-data/ "https://$P/" https://www.cloudimg.co.uk/; do
  printf '%s %s\n' "$(sudo -u spiffarena curl -sk -m 8 -o /dev/null -w '%{http_code}' "$u" || true)" "${u/$P/PRIVATE_IP}"
done | tee /dev/stderr | awk '{print $1}' | xargs | grep -qx '000 000 200' && echo "fenced: metadata and private refused, public HTTPS allowed"
000 http://169.254.169.254/latest/meta-data/
000 https://PRIVATE_IP/
200 https://www.cloudimg.co.uk/
fenced: metadata and private refused, public HTTPS allowed

Inside SpiffArena a script task that tries to connect to a fenced address gets a timeout (the systemd layer drops the packets) or a refusal, and the task fails with that error; the same script reaches public addresses normally.

To let SpiffArena reach a service in your own network, open just that range (metadata, link-local and loopback ranges are always refused). The helper updates both layers and restarts SpiffArena:

sudo spiffarena-egress-allow add 10.20.30.40/32
sudo spiffarena-egress-allow list
sudo spiffarena-egress-allow remove 10.20.30.40/32

Step 8: What the front door refuses

nginx answers only for the server's own names and passes requests to SpiffArena on loopback:

H="$(sudo sed -n 's/^SPIFFARENA_HOST=//p' /root/spiffarena-credentials.txt)"
{ curl -s -o /dev/null -w '%{http_code}\n' --cacert /etc/ssl/spiffarena/spiffarena.crt --resolve "$H:443:127.0.0.1" "https://$H/"
  curl -s -o /dev/null -w '%{http_code}\n' -k -H 'Host: someone-else.example' https://127.0.0.1/
  curl -s -o /dev/null -w '%{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
  curl -s -o /dev/null -w '%{http_code}\n' --cacert /etc/ssl/spiffarena/spiffarena.crt --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' "https://$H/v1.0/status"
} | xargs | tee /dev/stderr | grep -qx '200 000 301 403' && echo "front door OK"
200 000 301 403
front door OK
  • Sign-in. The sign-in routes are limited to 60 a minute per client address, for every spelling of the paths. The OpenID provider's token and refresh endpoints, the metrics endpoint and the webhooks are refused from the network.
  • X-Forwarded-For. nginx replaces any X-Forwarded-For header with the address it accepted the connection from and blanks X-Real-IP, Forwarded and similar headers, so a client cannot change the address the rate limits and logs see.
  • Cross-site requests. Requests whose Origin is not the instance's own address get 403, so a web page elsewhere cannot use a signed-in user's session.
  • Sizes and types. Request bodies to the API (/v1.0/, which carries file uploads) are limited to 64 MiB, sign-in forms to 64 KiB and everything else to 16 MiB. Compressed request bodies (Content-Encoding) get 415 and TRACE gets 405.
  • Headers. Responses carry Strict-Transport-Security, X-Content-Type-Options: nosniff, Referrer-Policy and X-Frame-Options, and nginx does not reveal its version.

What is logged: nginx's access log records the client address, request path and status, without query strings. SpiffArena's API keeps no access log.

Step 9: Run the self-test, change the password

The image ships a self-test that checks that the upstream demo logins and a wrong password are refused, that your administrator signs in through the login gate (and that the gate refuses a missing, altered or borrowed cookie), that the token, refresh, metrics and webhook routes are refused from the network, that unauthenticated and cross-site writes are refused, and that the example process runs to completion:

sudo spiffarena-selftest | tail -n 1 | tee /dev/stderr | grep -q '^SPIFFARENA_SELFTEST_OK '
SPIFFARENA_SELFTEST_OK SpiffArena 4.0.0 at https://ec2-203-0-113-10.compute-1.amazonaws.com/: the upstream demo logins and a wrong password are refused, spiffadmin-3f9a1c signs in through the login gate (login_return refused without its cookie, with an altered cookie, for another user and off-site), the token/refresh back channel, metrics and webhooks are refused in every spelling, unauthenticated and cross-site writes are refused, process instance 2 of cloudimg-demo:hello-world ran to completion (answer 42)

To give the administrator a new random password and write it to the credentials file:

sudo spiffarena-rotate-password

Step 10: Use your own domain

Point a DNS name at the instance (an Elastic IP keeps it stable), then make it the server's address. This generates a new certificate for that name, pins nginx to it and tells SpiffArena its public address:

sudo spiffarena-set-site spiff.example.com

To use a certificate from a public CA instead, for example one issued by certbot with a DNS challenge, run spiffarena-set-site first, then replace /etc/ssl/spiffarena/spiffarena.crt (full chain) and /etc/ssl/spiffarena/spiffarena.key and run sudo systemctl reload nginx.

Step 11: Back up and restore

Everything SpiffArena stores is in /var/lib/spiffarena: the SQLite database db.sqlite3 (instances, tasks, users, history) and the process models in process-models. The configuration and keys are in /etc/spiffarena and /etc/spiffarena-authgate. Take a consistent copy of the database with SQLite's online backup, then archive the models:

sudo -u spiffarena sqlite3 /var/lib/spiffarena/db.sqlite3 ".backup /var/lib/spiffarena/backup-$(date +%F).sqlite3"
sudo tar -C /var/lib/spiffarena -czf spiffarena-$(date +%F).tar.gz backup-$(date +%F).sqlite3 process-models

Copy the archive off the instance, for example to Amazon S3. To restore on a new instance of this image: sudo systemctl stop spiffarena spiffarena-scheduler, put the database back as /var/lib/spiffarena/db.sqlite3 and the models under /var/lib/spiffarena/process-models (owned by spiffarena), then sudo systemctl start spiffarena spiffarena-scheduler. The new instance keeps its own administrator, keys and certificate, so everyone signs in again. Copy any accounts you added to /etc/spiffarena/permissions.yml into the new instance's file, and re-enter the values under Configuration > Secrets: SpiffArena encrypts them with the instance's own key.

Step 12: Security notes and the build record

The certificate generated at first boot is a server certificate only: it cannot sign other certificates.

openssl x509 -in /etc/ssl/spiffarena/spiffarena.crt -noout -ext basicConstraints,keyUsage,extendedKeyUsage | tee /dev/stderr | grep -q 'CA:FALSE'
X509v3 Basic Constraints: critical
    CA:FALSE
X509v3 Key Usage: critical
    Digital Signature, Key Encipherment
X509v3 Extended Key Usage: 
    TLS Web Server Authentication

The build record names the release, its licences and the dependencies raised to fixed releases:

sed -n '1,2p' /usr/share/doc/cloudimg-spiffarena/BUILD-INFO.txt | tee /dev/stderr | grep -q '^SpiffArena  *4\.0\.0 '
SpiffArena        4.0.0 (github.com/sartography/spiff-arena tag v4.0.0), LGPL-2.1; SpiffWorkflow 902055aa5c214581c4ca01bbe7dbed79c985c65f
                  LGPL-3.0; web interface with bpmn-js / dmn-js (MIT + bpmn.io watermark clause). SOURCE.txt
  • Who may edit process models. A script task is Python that runs as the spiffarena account, inside the API. Anyone allowed to create or edit process models can therefore read what that account can read, including the database and SpiffArena's own keys. The egress fence keeps such code off the metadata service and your network, but grant model editing to administrators only, and give everyone else permission to start processes and complete tasks.
  • Isolation. SpiffArena runs as its own unprivileged user spiffarena with no login shell and no capabilities, a read-only view of the system except /var/lib/spiffarena, hidden process lists and a memory limit. The login gate runs as a separate user, spiffgate, that has no network access beyond loopback, and spiffarena cannot read the gate's key.
  • Licences. SpiffArena is licensed LGPL-2.1 and SpiffWorkflow LGPL-3.0. The diagram editors are bpmn-js and dmn-js, licensed MIT with the bpmn.io condition that their watermark stays visible; the image keeps it. The licences are in /usr/share/doc/cloudimg-spiffarena.

Step 13: Keep it up to date

Ubuntu security updates, including nginx and SQLite, install automatically. To apply everything now:

sudo apt-get update && sudo apt-get upgrade -y

New SpiffArena releases arrive as new versions of the AWS Marketplace listing: launch the new version, restore your database and process models into it (Step 11) and run sudo spiffarena-set-site with your domain if you use one.

Support

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk

The other versions on this listing are on the developer and API tools guide.