Inbucket 3.2.0 on AWS User Guide
Overview
This guide covers launching and using the Inbucket 3.2.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.
Inbucket is an open source disposable email server for development and testing. Point an application's SMTP settings at it and every message the application sends is captured instead of delivered: Inbucket accepts mail for any address, never relays it anywhere, and shows each message in a web UI (HTML, plain text, source and attachments), over a REST API for automated tests, over POP3, and live on a Monitor page as it arrives. It is a safe place to test sign up, password reset and notification emails without real mail ever leaving your environment.
Inbucket has no login of its own (anyone who can reach its web page can read every captured message), so the cloudimg image locks it down:
- HTTPS from the first boot. nginx serves Inbucket on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- A password unique to your instance, on everything. HTTP Basic Auth is required for the user
adminon every page, the REST API, the live monitor and the attachments. The password is generated on first boot and written to/var/lib/cloudimg/inbucket-credentials.txt(root only); nginx stores only a bcrypt hash of it. No password is baked into the image. - SMTP stays on the instance until you open it. SMTP (port 2500), POP3 (1100) and Inbucket's own web server (9000) listen on 127.0.0.1 only, and the security group needs only 22, 80 and 443. Step 8 shows how to accept mail from your own application servers, and only from them.
- A flood of mail cannot fill the disk. Messages live in a 1 GiB mail store of their own (created on first boot and reserved up front). When it is full, Inbucket answers new mail with a temporary SMTP error and the rest of the instance is unaffected. Messages declared larger than 10,240,000 bytes and recipients past 200 per message are refused, each mailbox keeps its newest 500 messages, and mail older than 72 hours is removed.
- The metadata service is out of reach. Inbucket makes no outbound requests (its optional Lua scripting extension, which can load an HTTP client, is switched off), and Inbucket and nginx are blocked from the EC2 instance metadata service at the kernel level.
- Built from source with a patched toolchain. The upstream 3.2.0 release binary was built with an older Go release and
golang.org/x/netversion that carry known advisories. This image builds Inbucket 3.2.0 from its release tag with Go 1.27.3 and a fixedgolang.org/x/net;govulncheckreports no vulnerabilities. No Go toolchain or compiler ships. - Hardened defaults. Inbucket runs as its own unprivileged user with no capabilities and a read only view of the system except its mail store. nginx refuses to start until first boot has generated the certificate and the password, drops requests for host names the instance does not serve, refuses cross site requests that delete or change mail, and the AMI requires IMDSv2.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Inbucket 3.2.0 (/opt/inbucket/inbucket, with its web UI in /opt/inbucket/ui/dist), 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 (Inbucket) and TCP 80 (redirects to HTTPS) from your own networks only.
- To capture mail from other servers, their CIDR range (Step 8). Inbucket accepts mail on the instance itself without any extra rule.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Inbucket 3.2.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (Inbucket itself needs little; a smaller instance works for a team's test mail), your key pair, a security group as above, and a subnet your application servers can reach.
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=inbucket}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Inbucket 3.2.0 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 and creates the mail store, then Inbucket and nginx start:
systemctl is-active inbucket-store inbucket nginx inbucket-firstboot
sudo test -f /var/lib/cloudimg/inbucket-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. Inbucket's POP3 (1100), SMTP (2500) and web server (9000) listen on 127.0.0.1 only:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | 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"))
127.0.0.1:1100 users:(("inbucket"))
127.0.0.1:2500 users:(("inbucket"))
127.0.0.1:9000 users:(("inbucket"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
Step 4: Get your URL and password
sudo cat /var/lib/cloudimg/inbucket-credentials.txt
The file is readable by root only and holds the URL, the user name, this instance's password and the mail ports:
INBUCKET_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
INBUCKET_USERNAME=admin
INBUCKET_PASSWORD=<INBUCKET_PASSWORD>
INBUCKET_SMTP=127.0.0.1:2500
INBUCKET_POP3=127.0.0.1:1100
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 Inbucket under your own DNS name, see Step 11.
Step 5: Sign in
Open the INBUCKET_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 11). Sign in as admin with the password from Step 4; the browser remembers the login for the session.
The home page describes this instance's mail setup and limits:

Type a mailbox name (the part of an address before the @, for example demo for demo@example.test) into the box at the top right and press Enter. The mailbox lists its messages, newest first; select one to read it as safe HTML or plain text, view its source, or delete it:

The Monitor tab shows every message as it arrives, in any mailbox, over a live connection:

The Status tab shows the configuration (listeners, store type, mailbox cap, retention) and live metrics:

Step 6: Send a test message and read it back
Any SMTP client on the instance can send to 127.0.0.1 port 2500 with no login and no TLS. For example, with Python:
python3 - <<'PY'
import smtplib
from email.message import EmailMessage
m = EmailMessage()
m["From"] = "My App <app@example.test>"
m["To"] = "demo@example.test"
m["Subject"] = "Hello from my app"
m.set_content("This message was captured by Inbucket.\n")
with smtplib.SMTP("127.0.0.1", 2500) as s:
s.send_message(m)
print("sent to demo@example.test")
PY
sent to demo@example.test
The message is now in the demo mailbox in the web UI. The image also has a self test that sends a uniquely tagged message, reads it back through nginx with your password, deletes it and checks the security settings:
sudo inbucket-selftest
SELFTEST-OK https 301 + health + 401 noauth + 401 wrong + web UI + SMTP 127.0.0.1:2500 -> REST API round trip cloudimg-selftest-<timestamp> + 403 cross-origin delete + delete + 444 unknown host
Step 7: Use the REST API and POP3 in your tests
Inbucket's REST API (reference) lists a mailbox (GET /api/v1/mailbox/<name>), returns a message with its headers, text and HTML (GET /api/v1/mailbox/<name>/<id>), its raw source (.../<id>/source), and deletes a message or a whole mailbox (DELETE). Every call needs the same login as the web UI. Here the password is read from the credentials file and passed to curl on standard input, so it never appears in the process list:
sudo bash -c '
F=/var/lib/cloudimg/inbucket-credentials.txt
P=$(grep "^INBUCKET_PASSWORD=" $F | cut -d= -f2-)
H=$(grep "^INBUCKET_URL=" $F | sed -E "s#^INBUCKET_URL=https://([^/]+)/.*#\1#")
api() { printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" "$@"; }
ID=$(api "https://$H/api/v1/mailbox/demo" | python3 -c "import sys,json; print(json.load(sys.stdin)[-1][\"id\"])")
api "https://$H/api/v1/mailbox/demo/$ID" | python3 -c "
import sys, json
d = json.load(sys.stdin)
print(\"from: \", d[\"from\"])
print(\"to: \", d[\"to\"])
print(\"subject:\", d[\"subject\"])
print(\"seen: \", d[\"seen\"])
print(\"body: \", d[\"body\"][\"text\"].strip())"
'
from: My App <app@example.test>
to: ['<demo@example.test>']
subject: Hello from my app
seen: False
body: This message was captured by Inbucket.
From another machine, call https://<your-inbucket-host>/api/v1/... with the same user and password (for example curl -u admin and let curl prompt for the password, or your test framework's HTTP client with Basic Auth). Reading a message never marks it seen or removes it; only PATCH (mark seen) and DELETE change mail.
POP3 accepts any password for a mailbox name and is on 127.0.0.1 only, which suits test code running on the instance (or through an SSH tunnel: ssh -i <your-key.pem> -L 1100:127.0.0.1:1100 ubuntu@<public-ip>):
python3 - <<'PY'
import poplib
p = poplib.POP3("127.0.0.1", 1100)
p.user("demo")
p.pass_("any-password")
count, size = p.stat()
print("messages:", count)
for line in p.retr(count)[1]:
if line.startswith((b"Subject:", b"From:")):
print(line.decode())
p.quit()
PY
messages: 1
From: My App <app@example.test>
Subject: Hello from my app
Clean up a mailbox between test runs with a DELETE:
sudo bash -c '
F=/var/lib/cloudimg/inbucket-credentials.txt
P=$(grep "^INBUCKET_PASSWORD=" $F | cut -d= -f2-)
H=$(grep "^INBUCKET_URL=" $F | sed -E "s#^INBUCKET_URL=https://([^/]+)/.*#\1#")
api() { printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" "$@"; }
api -o /dev/null -w "delete mailbox demo -> HTTP %{http_code}\n" -X DELETE "https://$H/api/v1/mailbox/demo"
echo "messages left: $(api "https://$H/api/v1/mailbox/demo")"
'
delete mailbox demo -> HTTP 200
messages left: []
Step 8: Accept mail from your application servers
Out of the box only programs on the instance itself can send mail to Inbucket. Inbucket accepts mail for any address from anyone who can connect, so open SMTP to the servers that should send test mail, and only to them. It takes two changes.
- Make Inbucket listen on all interfaces. Local settings go in
/etc/inbucket/inbucket.local.env, which is read after the shipped/etc/inbucket/inbucket.env(so its values win and survive image updates). Then restart Inbucket and check the listener:
# then open TCP 2500 to <your-mgmt-cidr> only (step 2)
echo 'INBUCKET_SMTP_ADDR=0.0.0.0:2500' | sudo tee /etc/inbucket/inbucket.local.env
sudo systemctl restart inbucket
sudo ss -lntp | grep ':2500 '
- Allow TCP 2500 in the instance's security group from your application servers' CIDR only (here
<your-mgmt-cidr>, for example10.20.0.0/16), never from0.0.0.0/0:
aws ec2 authorize-security-group-ingress --group-id <your-security-group> \
--protocol tcp --port 2500 --cidr <your-mgmt-cidr>
Then set your application's SMTP settings to the instance's private IP or DNS name, port 2500, no authentication and no TLS (Inbucket's STARTTLS is off). Mail from those servers now appears in the web UI.
To go back to local only, remove the override, restart Inbucket and delete the security group rule:
sudo rm -f /etc/inbucket/inbucket.local.env
sudo systemctl restart inbucket
# and remove the security group rule for <your-mgmt-cidr>
The same file takes any other Inbucket setting from its configuration reference, for example INBUCKET_SMTP_ACCEPTDOMAINS with INBUCKET_SMTP_DEFAULTACCEPT=false to accept mail only for your test domains. Keep POP3 and the web server on 127.0.0.1: nginx already serves the web UI and API with HTTPS and the login.
Step 9: Limits and the mail store
The limits Inbucket is running with:
sudo bash -c '
F=/var/lib/cloudimg/inbucket-credentials.txt
P=$(grep "^INBUCKET_PASSWORD=" $F | cut -d= -f2-)
H=$(grep "^INBUCKET_URL=" $F | sed -E "s#^INBUCKET_URL=https://([^/]+)/.*#\1#")
printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" "https://$H/serve/status" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(json.dumps({k: d[k] for k in (\"version\", \"pop3-listener\", \"web-listener\")}, indent=1)); print(json.dumps(d[\"smtp-config\"][\"addr\"])); print(json.dumps(d[\"storage-config\"], indent=1))"
'
grep -E '^INBUCKET_(SMTP_MAX|STORAGE_RET|STORAGE_MAIL|LUA)' /etc/inbucket/inbucket.env
{
"version": "3.2.0",
"pop3-listener": "127.0.0.1:1100",
"web-listener": "127.0.0.1:9000"
}
"127.0.0.1:2500"
{
"mailbox-msg-cap": 500,
"store-type": "file",
"retention-period": "72h0m0s"
}
INBUCKET_LUA_PATH=
INBUCKET_SMTP_MAXRECIPIENTS=200
INBUCKET_SMTP_MAXMESSAGEBYTES=10240000
INBUCKET_STORAGE_RETENTIONPERIOD=72h
INBUCKET_STORAGE_MAILBOXMSGCAP=500
| Limit | Value | What happens |
|---|---|---|
| Message size | 10,240,000 bytes | A client that declares a larger size is refused with 552 (Python, Postfix and most libraries declare it); the Inbucket service also has a memory ceiling |
| Recipients per message | 200 | The 201st RCPT TO is refused with 552 |
| Messages per mailbox | 500 | The oldest message is removed to make room |
| Retention | 72 hours | Older messages are removed (checked every minute) |
| Mail store | 1 GiB | When full, new mail gets 451 (try again later); nothing else on the instance runs out of disk |
The mail store is an ext4 filesystem in /var/lib/inbucket-store.img (created on this instance's first boot, with its space reserved up front), mounted at /var/lib/inbucket with nodev,nosuid,noexec:
findmnt -no FSTYPE,OPTIONS,TARGET /var/lib/inbucket
df -h --output=size,used,avail,target /var/lib/inbucket
sudo ls -ls --block-size=M /var/lib/inbucket-store.img
ext4 rw,nosuid,nodev,noexec,relatime /var/lib/inbucket
Size Used Avail Mounted on
974M 28K 958M /var/lib/inbucket
1009M -rw------- 1 root root 1024M <date> /var/lib/inbucket-store.img
To change a limit, put the setting in /etc/inbucket/inbucket.local.env (Step 8), for example INBUCKET_STORAGE_RETENTIONPERIOD=24h, and restart Inbucket. To grow the store (here to 4 GiB; make sure the root volume has the room, and grow the EBS volume first if needed), stop Inbucket and the store, resize the image and its filesystem, and start them again:
# keep a copy of any mail you need first: sudo cp -a /var/lib/inbucket/mail <backup-dir>
sudo systemctl stop inbucket inbucket-store
sudo fallocate -l 4GiB /var/lib/inbucket-store.img
sudo e2fsck -f -p /var/lib/inbucket-store.img
sudo resize2fs /var/lib/inbucket-store.img
sudo sed -i 's/^STORE_SIZE_MIB=.*/STORE_SIZE_MIB=4096/' /etc/inbucket/store.conf
sudo systemctl start inbucket-store inbucket
df -h /var/lib/inbucket
Step 10: Check the security posture from the command line
Without a login everything is refused, including the REST API, deletes and the monitor connection; 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 '^INBUCKET_URL=' /var/lib/cloudimg/inbucket-credentials.txt | sed -E 's#^INBUCKET_URL=https://([^/]+)/.*#\1#')
R="--resolve $H:443:127.0.0.1 --resolve $H:80:127.0.0.1"
curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' $R "http://$H/"
curl -sk -o /dev/null -w 'web UI, no login -> HTTP %{http_code}\n' $R "https://$H/"
curl -sk -o /dev/null -w 'REST API, no login -> HTTP %{http_code}\n' $R "https://$H/api/v1/mailbox/demo"
curl -sk -o /dev/null -w 'delete, no login -> HTTP %{http_code}\n' $R -X DELETE "https://$H/api/v1/mailbox/demo"
curl -sk --http1.1 -o /dev/null -w 'monitor WebSocket, no login -> HTTP %{http_code}\n' $R -H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' "https://$H/api/v2/monitor/messages"
curl -sk -o /dev/null -w 'health -> HTTP %{http_code}\n' $R "https://$H/health"
curl -sk -o /dev/null -w 'unknown host -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" "https://evil.example/" || true
http:// -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
web UI, no login -> HTTP 401
REST API, no login -> HTTP 401
delete, no login -> HTTP 401
monitor WebSocket, no login -> HTTP 401
health -> HTTP 200
unknown host -> 000
000 means nginx closed the connection without answering, which is how it treats any host name it does not serve.
With the password, Inbucket is served and works from its own pages. A delete, or a monitor connection, whose browser Origin is another web site is refused (HTTP 403), so another page cannot use your signed in browser to read the live feed or delete captured mail:
sudo bash -c '
F=/var/lib/cloudimg/inbucket-credentials.txt
P=$(grep "^INBUCKET_PASSWORD=" $F | cut -d= -f2-)
H=$(grep "^INBUCKET_URL=" $F | sed -E "s#^INBUCKET_URL=https://([^/]+)/.*#\1#")
api() { printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null "$@"; }
ws() { api --http1.1 -m 3 -w "$1 -> HTTP %{http_code}\n" -H "Origin: $2" -H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" "https://$H/api/v2/monitor/messages"; }
api -w "with login -> HTTP %{http_code}\n" "https://$H/"
api -w "delete, other origin -> HTTP %{http_code}\n" -X DELETE -H "Origin: https://evil.example" "https://$H/api/v1/mailbox/demo"
api -w "delete, same origin -> HTTP %{http_code}\n" -X DELETE -H "Origin: https://$H" "https://$H/api/v1/mailbox/demo"
ws "monitor WebSocket, other origin" "https://evil.example"
ws "monitor WebSocket, same origin" "https://$H" || true
'
with login -> HTTP 200
delete, other origin -> HTTP 403
delete, same origin -> HTTP 200
monitor WebSocket, other origin -> HTTP 403
monitor WebSocket, same origin -> HTTP 101
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/.inbucket-htpasswd
sudo stat -c '%a %U:%G %n' /etc/nginx/.inbucket-htpasswd /var/lib/cloudimg/inbucket-credentials.txt
admin:$2y$12$<bcrypt-hash>
640 root:www-data /etc/nginx/.inbucket-htpasswd
600 root:root /var/lib/cloudimg/inbucket-credentials.txt
The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^INBUCKET_URL=' /var/lib/cloudimg/inbucket-credentials.txt | sed -E 's#^INBUCKET_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
Inbucket and nginx cannot reach the instance metadata service; Inbucket runs as its own user with no capabilities and can write only to its mail store; its Lua extension is off and its listeners are on 127.0.0.1:
systemctl show inbucket nginx -p IPAddressDeny
systemctl show inbucket -p User -p ProtectSystem -p ReadWritePaths -p CapabilityBoundingSet -p NoNewPrivileges
sudo cat /proc/$(systemctl show -p MainPID --value inbucket)/environ | tr '\0' '\n' | grep -E '^INBUCKET_(LUA_PATH|SMTP_ADDR|POP3_ADDR|WEB_ADDR)='
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
CapabilityBoundingSet=
User=inbucket
ReadWritePaths=/var/lib/inbucket
ProtectSystem=strict
NoNewPrivileges=yes
INBUCKET_LUA_PATH=
INBUCKET_SMTP_ADDR=127.0.0.1:2500
INBUCKET_POP3_ADDR=127.0.0.1:1100
INBUCKET_WEB_ADDR=127.0.0.1:9000
Step 11: Your own DNS name and certificate
Point a DNS record at the instance, then set the name Inbucket is served as. This mints a new self signed certificate for the name, updates nginx, the cross site protection and the URL in the credentials file, and keeps the name across reboots:
sudo inbucket-set-hostname <your-domain>
To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running inbucket-set-hostname again replaces it with a self signed one):
sudo install -m 0644 <your-domain>.crt /etc/ssl/inbucket/inbucket.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/inbucket/inbucket.key
sudo nginx -t && sudo systemctl reload nginx
Step 12: 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/.inbucket-htpasswd admin
sudo systemctl reload nginx
Running sudo htpasswd -B -C 12 /etc/nginx/.inbucket-htpasswd <user> without -i prompts for the password instead. Use it with another user name to give a colleague or a CI system its own login, and sudo htpasswd -D /etc/nginx/.inbucket-htpasswd <user> to remove one. Then update or delete INBUCKET_PASSWORD in /var/lib/cloudimg/inbucket-credentials.txt so it does not hold an old password.
nginx checks the login and does not pass it on to Inbucket.
Step 13: Server components
| Component | Path |
|---|---|
| Inbucket binary | /opt/inbucket/inbucket |
| Web UI | /opt/inbucket/ui/dist |
| Configuration | /etc/inbucket/inbucket.env (shipped), /etc/inbucket/inbucket.local.env (yours), /etc/inbucket/greeting.html (home page) |
| Mail store | /var/lib/inbucket-store.img, mounted at /var/lib/inbucket (inbucket-store.service, size in /etc/inbucket/store.conf) |
| Inbucket service | inbucket.service (user inbucket; web 127.0.0.1:9000, SMTP 127.0.0.1:2500, POP3 127.0.0.1:1100) |
| Build record, licences, govulncheck reports, web UI checksums | /usr/share/doc/cloudimg-inbucket/ |
| nginx site | /etc/nginx/sites-available/cloudimg-inbucket |
| Basic Auth file | /etc/nginx/.inbucket-htpasswd (bcrypt) |
| Certificate and key | /etc/ssl/inbucket/ |
| First boot script | /usr/local/sbin/inbucket-firstboot.sh |
| Host name helper | /usr/local/sbin/inbucket-set-hostname |
| Self test | /usr/local/sbin/inbucket-selftest |
| Credentials | /var/lib/cloudimg/inbucket-credentials.txt (mode 0600) |
/opt/inbucket/inbucket -version
command -v go || echo "no Go toolchain"
nginx -v 2>&1
grep -h '^No vulnerabilities' /usr/share/doc/cloudimg-inbucket/govulncheck-inbucket-*.txt
grep -E '^(inbucket|toolchain|module bump) ' /usr/share/doc/cloudimg-inbucket/BUILD-INFO.txt
3.2.0
no Go toolchain
nginx version: nginx/1.24.0 (Ubuntu)
No vulnerabilities found.
No vulnerabilities found.
inbucket 3.2.0 (tag v3.2.0, commit 742249691d2f8f40253411394a8bbb8e50561849), MIT; source sha256 40035d9430da76d614bc6f09dc9c501bb857ed7b6d991c7374b9aea24d2e66ff
toolchain go1.27.3 (https://go.dev/dl/go1.27.3.linux-amd64.tar.gz, sha256 65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40); build only, not shipped
module bump golang.org/x/net@v0.61.0 golang.org/x/sys@v0.49.0 golang.org/x/text@v0.43.0 (go.mod/go.sum after the bump pinned by sha256; go.mod copy in this directory)
/usr/share/doc/cloudimg-inbucket/BUILD-INFO.txt records the exact source archive and commit, the Go toolchain, the module versions raised to fixed releases, the web UI's source and checksums, and the binary checksum; the govulncheck-*.txt files hold the full binary and source mode reports.
Step 14: Security notes and recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only, and TCP 2500 (if you open SMTP, Step 8) from your application servers only. Inbucket accepts mail for any address and is meant for test traffic: never point real users' mail at it or list it as an MX record.
- Keep the per instance password secret, and rotate it if it may have been shared (Step 12). Anyone with it can read every captured message, which can include password reset links and other tokens your applications send in test.
- Use your own DNS name and a certificate your clients trust (Step 11).
- Inbucket's SMTP and POP3 have no login and no TLS by design. Keep them on trusted networks.
- Inbucket's web UI is the upstream release's prebuilt single page app; it has no runtime JavaScript dependencies beyond its own compiled code, the web components polyfills, Font Awesome and Open Sans. The advisories some scanners report for the upstream
ui/yarn.lockbelong to the Elm compiler's installer, which is build tooling and is not in the app. cloudimg rechecks this at each Inbucket release. - Keep the operating system patched; unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else.
Support and licensing
Inbucket is open source under the MIT licence (James Hillyerd); the licence text is in /usr/share/doc/cloudimg-inbucket/. Its web UI includes Font Awesome Free (icons CC BY 4.0, fonts SIL OFL 1.1, code MIT) and Open Sans (Apache 2.0). There is no per user or per CPU 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