W3
Developer Tools AWS

wastebin 3.9.0 on AWS User Guide

| Product: wastebin 3.9.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the wastebin 3.9.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 subscribe and launch.

wastebin is a minimal, fast pastebin you host yourself. Paste text or code into a web form and get back a short link to share. Pastes get syntax highlighting for more than 170 languages, Markdown pastes render as formatted pages (tables, task lists, notes), and each paste can expire, be deleted the first time it is read (burn after reading) or be encrypted with a password. There are QR codes for opening a paste on a phone, a download and raw view, and a small JSON API for scripts. wastebin is a single program with an embedded SQLite database, so it starts instantly and needs very little memory.

The cloudimg image makes it safe to run on the internet:

  • Never an open pastebin. An anonymous pastebin that anyone on the internet can write to is soon used to host malware and phishing pages. The whole site therefore sits behind an HTTP Basic Auth prompt whose password is generated on your instance's first boot. The image ships with an empty password file, so if anything went wrong at first boot nobody, not even a guess, gets in. Share the gate password with the people who should use your pastebin; Step 10 shows how to open the instance to the public if you really want that.
  • HTTPS from the first boot. nginx serves wastebin on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • Per-instance secrets. wastebin's signing key (which lets the creator of a paste delete it) and the salt it uses to turn paste passwords into encryption keys are generated on your instance's first boot and kept in a file only root can read. Upstream wastebin otherwise uses a random key that changes at every restart, and the same fixed salt everywhere.
  • Pastes expire. The web form offers 10 minutes, 1 hour, 1 day (the default), 1 week and 1 month, with no "never" option, and expired pastes are deleted from the database once a day. Pastes are limited to 4 MiB, and nginx limits how fast any one address can send requests.
  • The host name comes from configuration, never from the request. The paste links and QR codes use the name set on first boot (the instance's public DNS name), nginx refuses requests for host names the instance does not serve, and the real client address is always the one nginx sees, whatever forwarding headers a request carries.
  • Other web sites cannot act with your login. nginx refuses any state changing request (creating, confirming or deleting a paste) whose browser Origin is not this instance. Reading a burn-after-reading paste through its raw or download link also deletes it, so those links refuse requests that another web site's page makes in your browser.
  • The application cannot reach the instance metadata service or anything else on the network. wastebin listens on a local socket only (nginx is its only client) and is allowed to open local sockets only; nginx and wastebin are blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6).
  • Built from source, with current fixes. wastebin 3.9.0 is built from its official release source (checksum pinned) with upstream's own Rust compiler release. Five of the libraries it is built with received security fixes after 3.9.0 was released; the image uses those fixed releases, and the exact dependency list is in /usr/share/doc/cloudimg-wastebin/Cargo.lock, with no known vulnerabilities at build time. There is no compiler on the image.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: wastebin 3.9.0 and wastebin-ctl (in /usr/local/bin, settings in /etc/wastebin, pastes in /var/lib/wastebin), 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.
  • A security group that allows TCP 22 (SSH), TCP 443 (wastebin) and TCP 80 (redirects to HTTPS). Open them to the networks your users work from. wastebin needs no other port.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick wastebin 3.9.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (wastebin itself needs very little, so a smaller instance also works for a small team), your key pair, a security group as above and a subnet. Pastes are stored on the root volume.

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=wastebin}]'

Step 2: Connect to your instance

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

Step 3: Check the services

First boot sets the host name and certificate and creates the secrets, then wastebin and nginx start:

systemctl is-active wastebin-firstboot wastebin nginx wastebin-purge.timer
sudo test -f /var/lib/cloudimg/wastebin-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 the network. wastebin listens on a local socket that only nginx can use:

sudo ss -lntpH | grep -v '127.0.0.5' | awk '{print $4, $6}' | sed -E 's/users:\(\("([^"]+)".*/\1/' | sort -u
sudo ss -lxpH | grep -F wastebin.sock | awk '{print $5, $9}' | sed -E 's/users:\(\("([^"]+)".*/\1/' | sort -u
0.0.0.0:22 sshd
0.0.0.0:443 nginx
0.0.0.0:80 nginx
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
/run/wastebin/wastebin.sock wastebin

Step 4: Get your URL and gate password

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

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

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

WASTEBIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
WASTEBIN_BASIC_USER=wastebin
WASTEBIN_BASIC_PASSWORD=<WASTEBIN_BASIC_PASSWORD>
#
# Open WASTEBIN_URL in a browser (self-signed certificate minted on this instance's first boot; port 80 redirects to
# https) and pass the HTTP Basic Auth prompt with WASTEBIN_BASIC_USER / WASTEBIN_BASIC_PASSWORD. wastebin has no
# accounts of its own: everyone who has the gate password can create pastes and read any paste whose link they have.
# Pastes expire (10 minutes to 1 month, default 1 day); expired pastes are deleted daily.
# Self-test:  sudo wastebin-selftest      Change the served name:  sudo wastebin-set-hostname <your-domain>
# User guide: https://www.cloudimg.co.uk/guides/wastebin-3-9-0-on-ubuntu-24-04-aws/

The URL uses the instance's public DNS name from EC2 instance metadata. Opening https://<public-ip>/ sends you on to that name.

wastebin has no user accounts: the gate password is what separates your users from everyone else, so give it only to the people who should use this pastebin, and change it if it leaks (Step 11).

The image includes a self test. Through nginx it passes the gate, creates, reads and deletes test pastes (a plain one, a password-protected one and a burn-after-reading one) and checks the security settings:

sudo wastebin-selftest
SELFTEST-OK https 301 + healthz + gate 401/401/200 + paste round trip (raw + view) + password paste refused without/with a wrong password, opened with it + burn-after-reading confirmed then gone after one read + owner-only delete (403 without the cookie) + 403 cross-site raw GET + 403 cross-origin POST + 444 unknown host

Step 5: Create a paste in the browser

Open the WASTEBIN_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 9). The browser then asks for a user name and password: enter WASTEBIN_BASIC_USER and WASTEBIN_BASIC_PASSWORD. The paste form appears. Paste or type your text, optionally give it a title, pick the language (type a few letters to filter the list), choose how long it should live and choose paste:

The wastebin paste form on cloudimg with a Python file pasted in, the title checksum.py, Python selected as the language, the expiry choices 10 mins, 1 hour, 1 day, 1 week and 1 month with 1 day selected, and the burn after reading and encrypt options

wastebin opens the new paste with syntax highlighting and line numbers. The address in your browser is the link to share. The buttons at the top right delete the paste (you created it, so your browser may), download it, show it raw, copy it and show a QR code:

A wastebin paste of the Python file checksum.py with syntax highlighting, line numbers, the time until it expires and the delete, download, raw, copy and QR code buttons

Choose Markdown as the language and the paste gets a rendered view, with headings, tables, task lists and notes:

A Markdown paste called Release checklist rendered by wastebin with a heading, a table of steps and owners, a task list and a note

A rendered Markdown paste shows images only when they are inline (data: URLs): this image does not let a paste make its readers' browsers fetch images from other web sites, a common way to track who opened a link.

Step 6: Burn after reading and password-protected pastes

Switch on burn after reading before you choose paste and wastebin shows you the link to send instead of the paste. When the recipient opens it, wastebin first asks them to confirm; the paste is deleted the moment they choose reveal, so it can be read exactly once:

The wastebin burn after reading confirmation for a paste called one-time DB URL, warning that the paste will be permanently deleted once revealed, with cancel and reveal buttons

Switch on encrypt and enter a password, and wastebin encrypts the paste with a key derived from that password before storing it. Anyone who opens the link must enter the password to see it; without it the paste cannot be read, even from the database file:

The wastebin encrypted paste prompt asking for the password, with cancel and decrypt buttons

Send the password through a different channel from the link.

Step 7: Use the command line and the API

wastebin has a small JSON API: send a POST to / with the text and options, and it answers with the path of the new paste. Pass the gate password with curl. On the instance itself:

H=$(sudo sed -n 's#^WASTEBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/cloudimg/wastebin-credentials.txt)
CFG=$(mktemp)   # the gate password goes into a private temporary curl config, never onto a command line
sudo sed -n 's/^WASTEBIN_BASIC_PASSWORD=\(.*\)/user = "wastebin:\1"/p' /var/lib/cloudimg/wastebin-credentials.txt > "$CFG"
P=$(printf '%s' '{"text":"hello from the wastebin API","extension":"txt","expires":3600}' \
  | curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -H 'Content-Type: application/json' --data-binary @- "https://$H/" \
  | python3 -c 'import json, sys; print(json.load(sys.stdin)["path"])')
curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: same-origin' "https://$H/raw$P"; echo
curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -o /dev/null -w 'paste view: HTTP %{http_code}\n' "https://$H$P"
rm -f "$CFG"
hello from the wastebin API
paste view: HTTP 200

The fields are text (required), extension (the language, for example py, rs, md), title, expires (seconds), burn_after_reading (true or false) and password. The answer also carries an owner token: opening https://<your-wastebin>/<id>?owner=<token> (with the token URL-encoded) in a browser lets that browser delete the paste.

From your own computer use the instance's URL and the gate credentials, keeping the password out of your shell history with a curl config file (user = "wastebin:<gate-password>", mode 600) passed with -K. Two notes:

  • The raw (/raw/<id>) and download (/dl/<id>) links answer only requests that show they come from the instance's own pages, because reading a burn-after-reading paste there deletes it. Browsers send that proof by themselves; with curl add -H 'Sec-Fetch-Site: same-origin' as above.
  • An API request can leave out expires, and the paste then never expires. The web form always sets an expiry. Everyone who can create pastes has the gate password, so this is a choice for your users rather than for strangers.

Burn after reading from the command line, read twice:

H=$(sudo sed -n 's#^WASTEBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/cloudimg/wastebin-credentials.txt)
CFG=$(mktemp)
sudo sed -n 's/^WASTEBIN_BASIC_PASSWORD=\(.*\)/user = "wastebin:\1"/p' /var/lib/cloudimg/wastebin-credentials.txt > "$CFG"
P=$(printf '%s' '{"text":"read me once","expires":600,"burn_after_reading":true}' \
  | curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -H 'Content-Type: application/json' --data-binary @- "https://$H/" \
  | python3 -c 'import json, sys; print(json.load(sys.stdin)["path"])')
echo "first read: $(curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: same-origin' "https://$H/raw$P")"
echo "second read: HTTP $(curl -sk -K "$CFG" --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: same-origin' -o /dev/null -w '%{http_code}' "https://$H/raw$P")"
rm -f "$CFG"
first read: read me once
second read: HTTP 404

Step 8: Expiry, size limit and storage

The settings are in /etc/wastebin/wastebin.conf:

grep -E '^WASTEBIN_(UNIX_SOCKET_PATH|MAX_BODY_SIZE|PASTE_EXPIRATIONS)=' /etc/wastebin/wastebin.conf
systemctl is-enabled wastebin-purge.timer
WASTEBIN_UNIX_SOCKET_PATH=/run/wastebin/wastebin.sock
WASTEBIN_MAX_BODY_SIZE=4194304
WASTEBIN_PASTE_EXPIRATIONS=10m,1h,1d=d,1w,1M
enabled

WASTEBIN_PASTE_EXPIRATIONS lists the choices the web form offers (m minutes, h hours, d days, w weeks, M months, y years; =d marks the default). Adding 0 would offer pastes that never expire. To change the choices or the 4 MiB size limit, edit the file (for the size limit also set the same number of bytes in client_max_body_size in /etc/nginx/sites-available/cloudimg-wastebin) and restart:

sudo nano /etc/wastebin/wastebin.conf
sudo systemctl restart wastebin && sudo nginx -t && sudo systemctl reload nginx

The pastes are in the SQLite database /var/lib/wastebin/state.db. wastebin-purge.timer deletes expired pastes from it once a day. To back it up, stop wastebin for a moment and copy the database:

sudo systemctl stop wastebin && sudo cp -a /var/lib/wastebin/state.db <backup-dir>/wastebin-$(date +%F).db; sudo systemctl start wastebin

Keep copies off the instance too, for example in Amazon S3, or take EBS snapshots of the volume. Password-protected pastes stay encrypted in the backup. The per-instance salt in /etc/wastebin/secrets.env is part of how their keys are made, so restore a database only together with that file.

Step 9: Use your own domain and certificate

  1. In your DNS, point <your-domain> (an A record) at the instance's public IP address. An Elastic IP keeps that address stable.
  2. Tell wastebin to use it. This sets the name in paste links and QR codes, the name nginx accepts and the guards, and generates a new self signed certificate for it:
sudo wastebin-set-hostname <your-domain>
  1. For a certificate your users' browsers trust, put the certificate chain for <your-domain> in /etc/ssl/wastebin/wastebin.crt and its private key in /etc/ssl/wastebin/wastebin.key (root only, mode 600), and run sudo systemctl reload nginx. Alternatively put the instance behind an Application Load Balancer with an AWS Certificate Manager certificate that forwards to port 443 on the instance.

Step 10: Open the pastebin to the public (opt-in)

Think twice before you do this. A pastebin that anyone on the internet can write to is soon found and used to host malware, phishing pages and stolen data, and you are responsible for what it serves. Keep the gate on unless you will watch what is pasted. If you still want to, switch the gate off and allow port 443 (and 80) from anywhere in the security group:

# anyone can now create and read pastes at https://<your-domain>/
sudo sed -i -E 's/^(auth_basic)/# \1/' /etc/nginx/wastebin-gate.conf && sudo nginx -t && sudo systemctl reload nginx

To put the gate back, remove the # in front of the two auth_basic lines of /etc/nginx/wastebin-gate.conf and reload nginx.

Step 11: Security notes

nginx, wastebin and the purge job are blocked from the instance metadata service:

for u in nginx wastebin wastebin-purge; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; done
nginx 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 
wastebin 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 
wastebin-purge 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 

The gate answers before wastebin does, nginx refuses a state changing request or a raw read that another web site's page sends, and a request for a host name the instance does not serve gets no answer at all:

H=$(sudo sed -n 's#^WASTEBIN_URL=https://\([^/]*\)/$#\1#p' /var/lib/cloudimg/wastebin-credentials.txt)
curl -sk -o /dev/null -w 'no password: %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'cross-origin POST: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Origin: https://evil.example' --data 'text=x' "https://$H/new"
curl -sk -o /dev/null -w 'cross-site raw read: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: cross-site' "https://$H/raw/x"
curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || true
no password: 401
cross-origin POST: 403
cross-site raw read: 403
unknown host name: 000

The secrets and the pastes are readable by root (and the pastes by the wastebin service user) only, and the build is recorded:

sudo stat -c '%a %U:%G %n' /etc/wastebin/secrets.env /var/lib/cloudimg/wastebin-credentials.txt /etc/nginx/wastebin.htpasswd /var/lib/wastebin
wastebin-ctl --version
sha256sum /usr/share/doc/cloudimg-wastebin/Cargo.lock | cut -c1-16
600 root:root /etc/wastebin/secrets.env
600 root:root /var/lib/cloudimg/wastebin-credentials.txt
640 root:www-data /etc/nginx/wastebin.htpasswd
700 wastebin:wastebin /var/lib/wastebin
wastebin-ctl 3.9.0
48ccb6677802dbdd

Other notes:

  • The gate password is a 24 character random string, stored as a SHA-512 crypt hash in /etc/nginx/wastebin.htpasswd. To change it, write a new hash there (printf '%s\n' '<new-password>' | openssl passwd -6 -stdin gives the hash; the line is wastebin:<hash>), update the credentials file and reload nginx.
  • Deleting pastes. The browser that created a paste can delete it (a signed cookie). There is no administrator view; on the instance, sudo -u wastebin wastebin-ctl list --database /var/lib/wastebin/state.db lists pastes and wastebin-ctl delete removes one by its id.
  • The build. /usr/share/doc/cloudimg-wastebin/BUILD-INFO.txt records the release, the compiler and the five dependency updates (ammonia, ctutils, zstd, zstd-safe and zstd-sys, each to the release with the published fix); Cargo.lock next to it lists every library in the program.
  • Logs. nginx logs are in /var/log/nginx; wastebin logs warnings and errors to the journal (journalctl -u wastebin).

Step 12: Keep it up to date

Ubuntu security updates install automatically. To apply all updates now:

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

New wastebin releases arrive as new versions of this listing, built and checked the same way. To move to one, back up the database and /etc/wastebin/secrets.env (Step 8), launch the new version and restore both. Read the wastebin changelog before you move.

Support and licensing

wastebin is open source under the MIT licence (Matthias Vogelgesang and contributors); the licence text is in /usr/share/doc/cloudimg-wastebin/, and the source code is at github.com/matze/wastebin. The Rust libraries it is built with are open source under permissive licences (MIT, Apache 2.0 and similar). There is no per user licence fee. The AMI price covers cloudimg's build, 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

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