Applications AWS

BentoPDF 2.8.10 on AWS User Guide

| Product: BentoPDF 2.8.10 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the BentoPDF 2.8.10 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg self-hosted personal apps listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

BentoPDF is an open source PDF toolkit with more than a hundred tools: merge and split, compress, organise, rotate, delete and extract pages, convert to and from images and office formats, add watermarks, page numbers and bookmarks, fill forms, sign, redact, compare, and change permissions and metadata. It is privacy first by design: every PDF operation runs inside your own web browser, so your documents are processed on your own machine and are never uploaded to the server. The server only delivers the web application.

Upstream BentoPDF has no login of its own. The cloudimg image turns it into a private PDF toolkit for your team:

  • HTTPS from the first boot. nginx serves BentoPDF on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • A password unique to your instance. HTTP Basic Auth is required for the user admin. The password is generated on first boot and written to /var/lib/cloudimg/bentopdf-credentials.txt (root only); nginx stores only a bcrypt hash of it. No password is baked into the image.
  • Fail closed. nginx refuses to start until first boot has generated the certificate and the password, so the toolkit is never served without a login.
  • Hardened defaults. Requests for any host name the instance does not serve are dropped, only GET and HEAD are accepted, cross site requests are refused, nginx is blocked from the instance metadata service and the AMI requires IMDSv2. BentoPDF's own Content Security Policy is served, which limits where the page may load code from and connect to.
  • Stock upstream release. The official BentoPDF 2.8.10 self hosted release bundle, checked against a pinned SHA-256 and unmodified, with its complete source code on the image.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: BentoPDF 2.8.10 (the static release in /var/www/bentopdf, with the source and licence in /usr/share/bentopdf), nginx, 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 (BentoPDF) and TCP 80 (redirects to HTTPS) from your own networks only.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick BentoPDF 2.8.10 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (BentoPDF itself needs very little on the server, because the PDF work happens in your users' browsers), 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=bentopdf}]'

Step 2: Connect to your instance

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

Step 3: Check the services

First boot generates the certificate and the password, then nginx starts:

systemctl is-active nginx bentopdf-firstboot
sudo test -f /var/lib/cloudimg/bentopdf-firstboot.done && echo FIRSTBOOT_DONE

Both units are active and the first boot marker is present:

active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on the network:

sudo ss -lntp | awk 'NR>1 && $4 !~ /^127\./ {print $4, $6}' | sort
0.0.0.0:22 users:(("sshd",...),("systemd",...))
0.0.0.0:443 users:(("nginx",...))
0.0.0.0:80 users:(("nginx",...))
[::]:22 users:(("sshd",...),("systemd",...))
[::]:443 users:(("nginx",...))
[::]:80 users:(("nginx",...))

Step 4: Get your URL and password

sudo cat /var/lib/cloudimg/bentopdf-credentials.txt

The file is readable by root only and holds the URL, the user name and this instance's password:

BENTOPDF_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
BENTOPDF_USERNAME=admin
BENTOPDF_PASSWORD=<BENTOPDF_PASSWORD>

The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve BentoPDF under your own DNS name, see Step 8.

Step 5: Sign in from your browser

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

The home page lists the tools as cards (Popular Tools first, then every category). Use the search box (Cmd+K or Ctrl+K) to jump to a tool:

The BentoPDF home page with the tool search box and cards for Edit PDF Text, the PDF Workflow Builder, PDF Multi Tool, Merge PDF, Split PDF, Compress PDF, PDF Editor, JPG to PDF, Sign PDF and Crop PDF

Step 6: Work with your PDFs

Merge PDF combines several documents into one. Drop the files on the page (or click to select them), drag the handle on a file to change the order, optionally type a page range such as 1-3, 5 for a file, then choose Merge PDFs. The merged file is built in your browser and downloaded straight away:

The BentoPDF Merge PDF tool with quarterly-report.pdf and appendix.pdf loaded, a page range box for each file and the Merge PDFs button

Compress PDF reduces the size of a document. Pick the algorithm and level (the recommended defaults suit most files) and choose Compress PDF:

The BentoPDF Compress PDF tool with merged.pdf loaded and the Condense algorithm and Balanced level selected

When it finishes, BentoPDF reports the method used and the size saved, and downloads the result:

The BentoPDF Compression Complete dialog reporting the Condense method and the reduced file size

Every tool has its own address, so you can bookmark or share a link such as https://<your-host>/merge-pdf or https://<your-host>/compress-pdf with your team (they sign in with the same password).

What leaves the browser. Your documents never do. Most tools use only the code served by your instance. A few heavy tools (some conversions, compression engines and OCR) download their open source WebAssembly engine from the public jsDelivr CDN the first time a browser uses them, and digital signature timestamping goes through BentoPDF's own proxy. The Content Security Policy served by the instance allows exactly those destinations. If your users' browsers have no internet access, the tools that need those engines will not run; everything else works.

Step 7: Check the security posture from the command line

Without a login the toolkit is refused, port 80 redirects to HTTPS, the health check stays open for load balancers, and a request for a host name the instance does not serve is dropped without an answer:

H=$(sudo grep '^BENTOPDF_URL=' /var/lib/cloudimg/bentopdf-credentials.txt | sed -E 's#^BENTOPDF_URL=https://([^/]+)/.*#\1#')
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 'no login -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'health -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/healthz"
curl -sk -o /dev/null -w 'unknown host -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" "https://evil.example/" || true
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/" | grep -o -m1 '401 Authorization Required'
http://  -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
no login -> HTTP 401
health -> HTTP 200
unknown host -> 000
401 Authorization Required

000 means nginx closed the connection without answering, which is how it treats any host name it does not serve.

With the password, BentoPDF is served. The password is passed to curl on standard input, so it never appears in the process list or your shell history:

sudo bash -c '
  F=/var/lib/cloudimg/bentopdf-credentials.txt
  P=$(grep "^BENTOPDF_PASSWORD=" $F | cut -d= -f2-)
  H=$(grep "^BENTOPDF_URL=" $F | sed -E "s#^BENTOPDF_URL=https://([^/]+)/.*#\1#")
  auth() { printf "user = \"admin:%s\"\n" "$P"; }
  auth | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w "with login -> HTTP %{http_code}\n" "https://$H/"
  auth | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w "merge tool -> HTTP %{http_code}\n" "https://$H/merge-pdf"
  auth | curl -K - -skI --resolve "$H:443:127.0.0.1" "https://$H/" | grep -iE "^cross-origin-(opener|embedder)-policy"
'
with login -> HTTP 200
merge tool -> HTTP 200
cross-origin-opener-policy: same-origin
cross-origin-embedder-policy: credentialless

nginx keeps only a bcrypt hash of the password; the plain text is in the root only credentials file:

sudo sed -E 's/\$2y\$12\$.*/$2y$12$<bcrypt-hash>/' /etc/nginx/.bentopdf-htpasswd
sudo stat -c '%a %U:%G %n' /etc/nginx/.bentopdf-htpasswd /var/lib/cloudimg/bentopdf-credentials.txt
admin:$2y$12$<bcrypt-hash>
640 root:www-data /etc/nginx/.bentopdf-htpasswd
600 root:root /var/lib/cloudimg/bentopdf-credentials.txt

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

H=$(sudo grep '^BENTOPDF_URL=' /var/lib/cloudimg/bentopdf-credentials.txt | sed -E 's#^BENTOPDF_URL=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>.<region>.compute.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
    DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

nginx cannot reach the instance metadata service, and it will not start until first boot has finished:

systemctl show nginx -p IPAddressDeny | grep 169.254.169.254
systemctl cat nginx | grep -E '^ExecStartPre=/usr/bin/test'
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
ExecStartPre=/usr/bin/test -f /var/lib/cloudimg/bentopdf-firstboot.done
ExecStartPre=/usr/bin/test -s /etc/nginx/.bentopdf-htpasswd

nginx also refuses a request whose Origin is another web site (HTTP 403), because browsers resend Basic Auth credentials on cross site requests:

H=$(sudo grep '^BENTOPDF_URL=' /var/lib/cloudimg/bentopdf-credentials.txt | sed -E 's#^BENTOPDF_URL=https://([^/]+)/.*#\1#')
curl -sk --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' -d x=1 "https://$H/" | grep -o -m1 '403 Forbidden'
403 Forbidden

Step 8: Your own DNS name and certificate

Point a DNS record at the instance, then set the name BentoPDF is served as. This mints a new self signed certificate for the name, updates nginx and the URL in the credentials file, and keeps the name across reboots:

sudo bentopdf-set-hostname <your-domain>

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

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

Step 9: Change the password or add users

The login is an nginx Basic Auth file. To set a new password for admin, hand it to htpasswd on standard input (printf is a shell builtin, so the password never appears in the process list; start the line with a space so most shells keep it out of your history), then reload nginx:

 printf '%s\n' '<new-password>' | sudo htpasswd -i -B -C 12 /etc/nginx/.bentopdf-htpasswd admin
sudo systemctl reload nginx

Running sudo htpasswd -B -C 12 /etc/nginx/.bentopdf-htpasswd <user> without -i prompts for the password instead. Use it with another user name to give a colleague their own login, and sudo htpasswd -D /etc/nginx/.bentopdf-htpasswd <user> to remove one. Then update or delete BENTOPDF_PASSWORD in /var/lib/cloudimg/bentopdf-credentials.txt so it does not hold an old password.

Step 10: Server components

Component Path
BentoPDF static release /var/www/bentopdf
Source and licence (AGPL-3.0) /usr/share/bentopdf/
nginx site /etc/nginx/sites-available/cloudimg-bentopdf
Security headers /etc/nginx/bentopdf-headers.conf
Basic Auth file /etc/nginx/.bentopdf-htpasswd (bcrypt)
Certificate and key /etc/ssl/bentopdf/
First boot script /usr/local/sbin/bentopdf-firstboot.sh
Host name helper /usr/local/sbin/bentopdf-set-hostname
Credentials /var/lib/cloudimg/bentopdf-credentials.txt (mode 0600)
ls -l /usr/share/bentopdf/ | awk 'NR>1{print $1,$5,$9}'
du -sh /var/www/bentopdf | awk '{print $1, $2}'
nginx -v 2>&1
-rw-r--r-- 34523 LICENSE
-rw-r--r-- 755 README.source.txt
-rw-r--r-- 151927366 bentopdf-v2.8.10-source.tar.gz
253M /var/www/bentopdf
nginx version: nginx/1.24.0 (Ubuntu)

Step 11: Security recommendations

  • Allow ports 22, 80 and 443 in the security group from your own networks only.
  • Keep the per instance password secret, and rotate it if it may have been shared (Step 9).
  • Use your own DNS name and a certificate your clients trust (Step 8).
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else.

Support and licensing

BentoPDF is open source under the GNU Affero General Public License v3.0; there is no per user or per CPU licence fee for running it under that licence. The image serves the unmodified upstream release, and the complete source of that exact version is in /usr/share/bentopdf/ (also at https://github.com/alam00000/bentopdf/tree/v2.8.10). If you modify BentoPDF and let others use it over a network, the AGPL requires you to offer them your modified source; BentoPDF's authors also sell a commercial licence for deployments that cannot meet that. 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