Applications AWS

Engelsystem 3.7.1 on AWS User Guide

| Product: Engelsystem 3.7.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Engelsystem 3.7.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg ERP and CRM listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Engelsystem is the open source shift planning system that the Chaos Communication Congress and many other festivals and conferences use to organise their volunteers, the "angels". Organisers define locations, angel types (the teams: info desk, kitchen, stage crew and so on) and shifts; angels sign up for the shifts they want, and the organisers see who has arrived, who is working and who is free, with news, meetings, questions to the organisers, worklogs and goodies (T-shirts and vouchers) on top.

What the cloudimg image adds:

  • No default login. Upstream's first account admin with the published password asdfasdf never exists on this image. First boot creates the administrator under a name unique to your instance (admin- and six hex digits) with a 24 character password generated on the instance, and writes both to /root/engelsystem-credentials.txt (root only).
  • Nothing shared between instances. The image ships no database, no configuration file and no key. First boot initialises MySQL, creates the database and its user with a fresh password, mints Engelsystem's app key and API key, runs Engelsystem's database migrations and generates the TLS certificate, all on your instance.
  • HTTPS from the first boot. nginx serves Engelsystem on port 443 with a certificate generated on your instance, port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped.
  • Fail closed. nginx and PHP start only after first boot has finished. If it cannot finish, nothing is served.
  • Closed by default. Public registration is off, outgoing e-mail goes to a log until you configure a mail server, no single sign on (OAuth) provider is configured and no schedule import is set up. Each is a setting you turn on when you need it (Step 7).
  • Rate limits. Engelsystem has no sign in throttle of its own, so nginx limits the sign in, registration and password reset pages to 10 requests a minute per client, every state changing request to 600 a minute and every page to 1800 a minute. A client is one IPv4 address or one IPv6 /64, taken from the connection, never from a header a client can forge.
  • Hardened defaults. nginx, PHP-FPM, MySQL and the first boot service are blocked from the instance metadata service and the AMI requires IMDSv2. Engelsystem's PHP runs as its own user with its code read only, and that user cannot open connections to the metadata service, to other hosts in your VPC or to local services unless you allow them. The two state changing links that a malicious page could trigger (sign out and the schedule reload) refuse cross site requests. The access log records paths only, never the query string that carries Engelsystem's API key.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Engelsystem 3.7.1 (the upstream release, checksum verified, with three of its PHP libraries raised to fixed releases, see Step 10), nginx 1.24, PHP 8.3 FPM with OPcache and MySQL 8.0 (all from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent. PHP is Ubuntu's 8.3.6-0ubuntu0.24.04.13 build or later.

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

The Engelsystem shift plan with shifts at the Infodesk and the Kitchen, the Sign up button for Kitchen angels and the location, angel type and occupancy filters

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 (Engelsystem) and TCP 80 (redirects to HTTPS) from the networks your angels and organisers work from. MySQL has no network port to open.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Engelsystem 3.7.1 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 root volume holds the database, so give it room to grow (for example 30 GiB or more for a large event).

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=30,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=engelsystem}]'

First boot takes about a minute after the instance is running.

Step 2: Connect to your instance

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

Step 3: Check the services

First boot initialises MySQL, creates the database, installs Engelsystem and the administrator and generates the certificate, then PHP-FPM and nginx start:

systemctl is-active nginx php8.3-fpm mysql engelsystem-egress-guard engelsystem-firstboot
sudo test -f /var/lib/cloudimg/engelsystem-firstboot.done && echo FIRSTBOOT_DONE

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

active
active
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on network ports. MySQL has no TCP listener and PHP-FPM uses a local socket:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v ':53 ' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
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"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))

Step 4: Get your URL, user name and password

sudo cat /root/engelsystem-credentials.txt

The file is readable by root only:

# Engelsystem 3.7.1 - generated on first boot by engelsystem-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret.

ENGELSYSTEM_URL=https://<public-dns-name>/login
ENGELSYSTEM_ADMIN_USER=admin-<6 hex digits>
ENGELSYSTEM_ADMIN_PASSWORD=<ENGELSYSTEM_ADMIN_PASSWORD>
ENGELSYSTEM_TLS_SHA256=<ENGELSYSTEM_TLS_SHA256>
#
# Open ENGELSYSTEM_URL in a browser (self-signed certificate minted on this instance's first boot; compare its SHA-256
# fingerprint with ENGELSYSTEM_TLS_SHA256; port 80 redirects to https) and sign in with the user name and password
# above (upstream's default admin / asdfasdf does not exist on this image).
# Public self-registration is OFF: tick Admin > Config > Event > Registration enabled when volunteers should sign up.
# /metrics needs the API key in /etc/engelsystem/config.php ('api_key'): https://<host>/metrics?api_key=<key>
# Database: MySQL 8.0 on this instance (unix socket only), database and user engelsystem; the password is in
# /etc/engelsystem/config.php. Outbound e-mail ships OFF (driver 'log', no SMTP credential): see the user guide.
# Services: nginx php8.3-fpm mysql engelsystem-egress-guard   Migrations: sudo engelsystem-migrate
# Change the password:            sudo engelsystem-set-password
# Change the name it serves as:   sudo engelsystem-set-hostname <your-dns-name>
# User guide: https://www.cloudimg.co.uk/guides/engelsystem-3-7-1-on-ubuntu-24-04-aws/

The URL uses the instance's public DNS name from the EC2 instance metadata (its public IP address when the subnet gives no DNS name). The certificate also names the public and private IP addresses. If you stop and start the instance without an Elastic IP, its public name changes: set the new one with engelsystem-set-hostname (Step 8), or use your own DNS name.

Step 5: Sign in

Browse to the URL from the credentials file. Your browser warns once about the self signed certificate: check that the SHA-256 fingerprint it shows matches ENGELSYSTEM_TLS_SHA256, then accept it (or install your own certificate, Step 8). Sign in with the user name (Nick) and password from the credentials file.

The Engelsystem sign in page with the Nick and Password fields, the Login button and the notice that registration is disabled

You can read the fingerprint on the instance too:

echo | openssl s_client -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -fingerprint -sha256

After signing in you land on News. The menu across the top holds News, Meetings, Shifts, Angel types, Locations, Ask Heaven (questions to the organisers) and Admin, and your own menu at the top right holds your shifts, your settings and sign out.

Step 6: Set up your event

The image starts with an empty event. The screenshots in this step show a small example event created for this guide.

  1. Admin > Config. Under Event set the event name, the welcome message and the build up, start, end and tear down dates. The other tabs hold the feature switches, the certificate requirements (driving licence, food hygiene), the shift settings and the goodie (T-shirt or voucher) settings.
  2. Locations. Add each place where angels work (Main Hall, Infodesk, Kitchen).
  3. Angel types. Add a type for each team. Mark a type as requiring an introduction when only angels you have briefed may take its shifts, and allow self sign up where anyone may.
  4. Admin > Shift types and Admin > Create shifts. Define your shift types, then create the shifts per location with the number of angels of each type they need.
  5. Admin > Group rights. Decide what each group (Angel, Shift Coordinator, Team Coordinator, Bureaucrat and so on) may do, and promote your organisers on their user pages under Admin > All Angels.

The Angel types page listing Infodesk Angel and Kitchen Angel with self sign up allowed and Stage Crew requiring an introduction

The Config page on the Event tab with the event name, welcome message, Registration enabled switch and the build up and event dates

During the event, Admin > Arrive angels records who has arrived, Admin > Active angels shows who has worked enough to earn a goodie, and Admin > Free angels lists the angels who are on site and not on a shift right now.

Step 7: Registration, e-mail, single sign on and schedule import

All four ship off. Turn on only what you need.

Registration. Angels cannot sign up on their own until you tick Admin > Config > Event > Registration enabled. Until then, create accounts yourself under Admin > All Angels (the + button).

E-mail. Engelsystem sends mail for password resets, shift changes and news. On this image the mail driver is log: nothing is sent, and each message is written to Engelsystem's log instead. To send mail, edit the email section of /etc/engelsystem/config.php (owned by root, readable by Engelsystem's group only) with your mail server, for example Amazon SES SMTP:

    'email' => [
        'driver' => 'smtp',
        'from' => [
            'address' => 'shifts@<your-domain>',
            'name' => 'Engelsystem',
        ],
        'host' => 'email-smtp.<region>.amazonaws.com',
        'port' => 587,
        'tls' => true,
        'username' => '<smtp-user>',
        'password' => '<smtp-password>',
    ],

then apply it:

sudo systemctl reload php8.3-fpm

Single sign on. Engelsystem can sign angels in with an OAuth 2 provider (Keycloak, Authentik, GitLab, your event's own identity system). Add an oauth section to /etc/engelsystem/config.php with the provider's client ID, client secret and its authorisation, token and user info URLs, as described in Engelsystem's config/app.php (/var/www/engelsystem/config/app.php), then reload PHP-FPM as above.

Schedule import. Admin > Import schedule reads a conference schedule (the Frab/Pentabarf XML format that pretalx and Frab publish) from a URL and creates the matching shifts. Add the schedule URL there when you want it. Engelsystem fetches the URL from the instance.

Servers inside your VPC. Engelsystem's PHP may connect to servers on the internet (your mail service, OAuth provider or schedule), but not to the instance metadata service, link local or reserved ranges, local services, or any address inside your own VPC. If your mail relay, OAuth provider or schedule server has a private address in this VPC (or you reach Amazon SES through a VPC interface endpoint), add it to the allow list, one address or CIDR per line (replace <private-ip> with that server's private address), and restart the guard:

echo '<private-ip>' | sudo tee -a /etc/cloudimg/engelsystem-egress-allow.conf
sudo systemctl restart engelsystem-egress-guard

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 Engelsystem is served as. This mints a new self signed certificate for the name, updates nginx, Engelsystem's configured URL (used in every link and e-mail) and the URL and fingerprint in /root/engelsystem-credentials.txt, and keeps the name across reboots:

sudo engelsystem-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 engelsystem-set-hostname again replaces it with a self signed one):

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

Step 9: Security checks

The shipped round trip checks that port 80 redirects to HTTPS, that the front controller prefix, the configuration and the sources are refused, that the health checks answer, that the administrator signs in through Engelsystem's own sign in form (with its CSRF token) with this instance's password while a wrong password and upstream's default are refused, and that /metrics needs the API key. Passwords are handed to the sign in helper in root only temporary files, never on a command line:

sudo /usr/local/sbin/engelsystem-cred-roundtrip
ENGELSYSTEM_ROUNDTRIP_OK https 301 + no index.php/config/sources + health + administrator sign-in (Engelsystem's own sign-in form, CSRF token) + wrong and default passwords refused + /metrics needs the API key

The configuration, the PHP libraries, the sources and the front controller prefix answer 404, plain HTTP redirects to HTTPS on the configured name, and a host name the instance does not serve gets no answer at all:

H=$(sudo grep '^ENGELSYSTEM_URL=' /root/engelsystem-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/.*#\1#')
for p in /login /healthz /index.php /index.php/login /config/config.php /vendor/autoload.php /composer.json /.env /bin/migrate; 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}\n' --resolve "$H:80:127.0.0.1" "http://$H/login"
curl -sk -o /dev/null -w 'unknown host name -> %{http_code}\n' --resolve "other.example:443:127.0.0.1" https://other.example/ || true
200  /login
200  /healthz
404  /index.php
404  /index.php/login
404  /config/config.php
404  /vendor/autoload.php
404  /composer.json
404  /.env
404  /bin/migrate
http:// -> 301
unknown host name -> 000

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

H=$(sudo grep '^ENGELSYSTEM_URL=' /root/engelsystem-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 = <public-dns-name>, O = cloudimg
X509v3 Subject Alternative Name:
    DNS:<public-dns-name>, 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 mysql engelsystem-firstboot -p Id -p IPAddressDeny
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128
Id=nginx.service

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

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

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

On top of that, an nftables table built at every boot stops Engelsystem's own user (the one PHP runs as) from reaching the metadata service, reserved ranges, local services and the addresses of your VPC, which it reads from the instance metadata at boot. Every other user on the instance is unaffected:

sudo nft list table inet cloudimg_es_egress | grep -E 'comment|elements'

The rate limits are keyed on the address of the connection (an IPv4 address, or the /64 network of an IPv6 address), so a client cannot dodge them with a forged X-Forwarded-For header. A client that goes over a limit gets HTTP 429 for a moment:

grep -h 'limit_req_zone' /etc/nginx/conf.d/cloudimg-engelsystem.conf
limit_req_zone $es_login_key zone=es_login:10m rate=10r/m;
limit_req_zone $es_post_key zone=es_post:10m rate=600r/m;
limit_req_zone $cloudimg_client_key zone=es_php:10m rate=1800r/m;

MySQL runs without networking, and Engelsystem's database user engelsystem can reach only its own database from the instance. root works only from the instance itself, as the operating system's root (sudo mysql):

sudo mysql -e "SELECT @@skip_networking, @@local_infile, @@log_bin, @@general_log; SELECT user, host, plugin FROM mysql.user ORDER BY user;"
@@skip_networking   @@local_infile  @@log_bin   @@general_log
1   0   0   0
user    host    plugin
engelsystem localhost   caching_sha2_password
mysql.infoschema    localhost   caching_sha2_password
mysql.session   localhost   caching_sha2_password
mysql.sys   localhost   caching_sha2_password
root    localhost   auth_socket

Engelsystem's Prometheus metrics at /metrics answer only with the API key from /etc/engelsystem/config.php:

H=$(sudo grep '^ENGELSYSTEM_URL=' /root/engelsystem-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/.*#\1#')
K=$(sudo php -r '$c = require "/etc/engelsystem/config.php"; echo $c["api_key"];')
curl -sk -o /dev/null -w 'without the key -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/metrics"
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/metrics?api_key=$K" | grep -c '^engelsystem_'

Without the key the answer is 403; with it, the second command counts the engelsystem_ metrics it returns:

without the key -> 403
<number of metrics>

Step 10: Versions and where things are

cat /var/www/engelsystem/storage/app/VERSION; echo
php -r 'echo PHP_VERSION."\n";'
mysqld --version
nginx -v
head -4 /usr/share/doc/engelsystem/SOURCE.txt
v3.7.1
8.3.6
/usr/sbin/mysqld  Ver 8.0.46-0ubuntu0.24.04.4 for Linux on x86_64 ((Ubuntu))
nginx version: nginx/1.24.0 (Ubuntu)
Engelsystem 3.7.1 on Ubuntu 24.04 by cloudimg - source and changes

Upstream: the Engelsystem 3.7.1 release zip, https://github.com/engelsystem/engelsystem/releases/download/v3.7.1/engelsystem-v3.7.1.zip
(sha256 1613d95b4746fec93c04344d789e524d3c0a6485561805686290e370e2832587; GitHub tag v3.7.1, commit 2ffb80f84a05f63b45dbf6707abff144ec1676eb).

Three of the PHP libraries bundled with the release were raised, inside the release's own version constraints, to versions that fix published advisories: symfony/http-foundation 7.4.19 to 7.4.23, symfony/mime 8.1.7 to 7.4.22 (the 8.1 line also needs PHP 8.4, and Ubuntu 24.04 ships 8.3) and symfony/yaml 7.4.18 to 7.4.23. composer audit of the shipped libraries reports no advisories. /usr/share/doc/engelsystem/SOURCE.txt lists every change.

Engelsystem's database migrations were written for MariaDB, and two of them give text columns a default value, which MySQL refuses in its default strict mode. /etc/engelsystem/config.php therefore sets Engelsystem's database session to MariaDB's default mode without strict mode ('modes' => ['ERROR_FOR_DIVISION_BY_ZERO', 'NO_ENGINE_SUBSTITUTION']). Keep that line if you edit the database section.

Component Path
Engelsystem (read only, owned by root) /var/www/engelsystem
Engelsystem configuration /etc/engelsystem/config.php (root, readable by the engelsystem group)
Engelsystem cache /var/lib/engelsystem/cache
MySQL database /var/lib/mysql (database engelsystem)
PHP-FPM pool /etc/php/8.3/fpm/pool.d/engelsystem.conf (runs as the user engelsystem)
nginx site and limits /etc/nginx/sites-available/cloudimg-engelsystem, /etc/nginx/conf.d/cloudimg-engelsystem.conf
Certificate and key /etc/nginx/ssl/
Logs /var/log/nginx/, /var/log/php8.3-fpm.log, /var/log/mysql/
Boot services /usr/local/sbin/engelsystem-firstboot.sh, /usr/local/sbin/engelsystem-egress-guard
Egress allow list /etc/cloudimg/engelsystem-egress-allow.conf
Helpers /usr/local/sbin/engelsystem-set-hostname, engelsystem-set-password, engelsystem-migrate, engelsystem-cred-roundtrip
Credentials /root/engelsystem-credentials.txt (mode 0600)

Step 11: Changing passwords

  • The administrator: change it in Engelsystem (your menu at the top right, Settings > Password), or on the instance. The helper below sets a new random password through Engelsystem's own password handling, removes any password reset links, signs every session out and writes the new password to /root/engelsystem-credentials.txt. To choose the password yourself, pipe it in with --stdin (20 to 72 letters and digits), so it never appears on a command line:
sudo engelsystem-set-password
sudo grep '^ENGELSYSTEM_ADMIN_PASSWORD=' /root/engelsystem-credentials.txt
  • Angels reset their own password from the sign in page (I forgot my password) once outgoing mail is set up (Step 7). The reset link always points at the configured URL, never at a host name taken from the request.

Step 12: Backup and maintenance

Everything Engelsystem stores is in the MySQL database engelsystem. The simplest backup is an EBS snapshot of the root volume. For a backup on the instance while Engelsystem is running:

sudo mysqldump --single-transaction --routines --triggers engelsystem | gzip > <backup-dir>/engelsystem-$(date +%F).sql.gz
sudo cp /etc/engelsystem/config.php <backup-dir>/engelsystem-config-$(date +%F).php

The configuration file holds the app key, the API key and the database password, so keep the copy as safe as the database. To restore into a fresh instance of the same version, stop PHP-FPM, load the dump and start it again:

sudo systemctl stop php8.3-fpm
gunzip -c <backup-dir>/engelsystem-<date>.sql.gz | sudo mysql engelsystem
sudo systemctl start php8.3-fpm

Operating system, nginx, PHP and MySQL security updates install automatically from Ubuntu; run sudo apt-get update && sudo apt-get upgrade to apply everything else. To move to a newer Engelsystem release, launch the newer version from the listing and restore your database into it (it applies any new migrations with sudo engelsystem-migrate), or contact cloudimg support. Take a snapshot first.

Step 13: Security recommendations

  • Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your angels and organisers use.
  • Use your own DNS name and a certificate your browsers trust (Step 8).
  • Give organisers their own accounts in the smallest group that fits, and keep the administrator for administration.
  • Turn registration on only while you are recruiting angels, and set up mail first so they can reset their passwords.
  • Change the administrator password if it may have been shared (Step 11), keep the operating system patched, and snapshot the volume regularly.
  • The compiled front end that ships in the Engelsystem release includes the JavaScript library deepmerge, which has two published advisories of low severity. It runs only in the browser on data the page itself builds, never on the server, and the release cannot be rebuilt without changing Engelsystem, so it ships as released.

Support and licensing

Engelsystem is open source software licensed under the GNU General Public License version 2 or later; cloudimg ships its code unmodified, with three bundled PHP libraries raised to fixed releases, and the source location, checksums and changes are in /usr/share/doc/engelsystem/SOURCE.txt. There is no per user licence fee. The AMI price covers cloudimg's hardening, first boot setup and support. Engelsystem is developed by its contributors; cloudimg is not affiliated with or endorsed by the Engelsystem project.

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