Storage AWS

Kinto 26.5.0 on AWS User Guide

| Product: Kinto 26.5.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Kinto 26.5.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg file sharing and sync listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Kinto is an open source JSON document store with sharing and synchronisation, originally built by Mozilla (it is the storage behind Firefox Remote Settings). Your applications keep JSON records in collections inside buckets through a REST API, with per object permissions, groups, change history timestamps and a sync protocol that lets offline clients pull only what changed. There is no user interface: you talk to it with curl, the kinto-http JavaScript and Python clients, or any HTTP library.

The cloudimg image locks Kinto down:

  • No default account. Upstream Kinto lets anyone create an account and write. This image ships no database at all: first boot creates the PostgreSQL cluster, the admin account with a password unique to your instance (stored as a bcrypt hash) and writes it to /root/kinto-credentials.txt (root only). Only admin may create accounts, buckets, collections, groups or records; anonymous writes and anonymous sign ups are refused.
  • HTTPS from the first boot. nginx serves the API on port 443 with a self signed certificate generated on your instance's first boot, and its SHA-256 fingerprint is in the credentials file so you can check it. Port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped.
  • No database password, no database port. PostgreSQL 16 listens on its local socket only (there is no TCP port 5432 at all) and Kinto signs in to it by operating system identity, so there is no database password to steal.
  • Per instance secrets. Kinto's HMAC secret (used for user ids and the sign in cache) and the TLS key are generated on each instance; nothing secret is baked into the image.
  • Browsers limited to your own origin. Kinto answers cross origin (CORS) requests from this server's own addresses only, instead of upstream's "any site". You add the origin of your own web app when you need one (Step 8).
  • Metadata service blocked. Kinto, nginx and PostgreSQL cannot reach the EC2 instance metadata service, and the AMI requires IMDSv2.
  • Pinned and patched. Kinto 26.5.0 and every one of its Python dependencies are installed from PyPI wheels pinned by SHA-256, and Ubuntu 24.04 LTS is fully patched at build time with unattended security upgrades switched on.

What is on the image: Kinto 26.5.0 (Apache-2.0) in a Python 3.12 virtual environment under /opt/kinto/venv, served by waitress behind nginx, PostgreSQL 16, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. The licence, the pinned package list and the build record are in /usr/share/doc/cloudimg/.

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) from your management network and TCP 443 (the Kinto API) and TCP 80 (redirects to HTTPS) from the networks your applications use.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Kinto 26.5.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair, a security group as above, and a subnet.

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

Step 2: Connect to your instance

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

Step 3: Check the services

First boot creates the database, the administrator and the certificate, then starts Kinto and opens the HTTPS port. It takes about half a minute:

systemctl is-active postgresql@16-main kinto nginx
active
active
active

Only SSH (22) and nginx (80, 443) listen on the network. Kinto listens on 127.0.0.1:8888 for nginx only, PostgreSQL has no TCP port at all, and the remaining loopback entries are Ubuntu's local DNS resolver:

sudo ss -lntp | awk 'NR>1 {print $4}' | sort -u
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:8888
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80

Kinto, nginx and PostgreSQL active; Kinto 26.5.0 from the pinned wheel; only 22, 80 and 443 on the network

If nginx is not running, first boot has not finished (or failed, in which case the API is never served). Check systemctl status kinto-firstboot and sudo cat /var/log/cloudimg/kinto-firstboot.log.

Step 4: Get your URL, password and certificate fingerprint

sudo cat /root/kinto-credentials.txt

The file is readable by root only:

# Kinto 26.5.0 - per-instance credentials, generated on first boot (cloudimg). Keep this file secret (0600 root).
# Sign in with HTTP Basic authentication. Change the password: sudo kinto-set-admin-password admin
...
KINTO_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/v1/
KINTO_ADMIN_USER=admin
KINTO_ADMIN_PASSWORD=<KINTO_ADMIN_PASSWORD>
KINTO_TLS_SHA256=AB:CD:...:EF

The built in self test checks the redirect, the host rules, an authenticated document round trip and every refusal (anonymous write, anonymous sign up, a wrong password, admin:admin, a foreign CORS origin):

sudo /usr/local/sbin/kinto-verify.sh
KINTO_ROUNDTRIP_OK admin auth + document round trip + anonymous/wrong/default refusals + CORS + host checks

Step 5: Say hello to the API

The root of the API is public and describes the server. On the instance, localhost is one of the certificate's names, so curl can check the certificate instead of skipping the check:

curl -s --cacert /etc/ssl/kinto/kinto.crt https://localhost/v1/ | python3 -m json.tool | head -18

The response names the server's address, the Kinto version and the one plugin this image enables (accounts):

{
    "project_name": "kinto",
    "project_version": "26.5.0",
    "http_api_version": "1.23",
    "project_docs": "https://kinto.readthedocs.io/",
    "url": "https://ec2-203-0-113-10.compute-1.amazonaws.com/v1/",
    "config": null,
    "settings": {
        "readonly": false,
        "batch_max_requests": 25,
        "explicit_permissions": true
    },
    "capabilities": {
        "accounts": {
            "description": "Manage user accounts.",

The per instance credentials (password masked) and the public /v1/ hello naming the accounts capability

From your own computer, first check that the certificate the instance presents is the one in the credentials file, then copy it and use it with --cacert (never add it to your system's trust store):

openssl s_client -connect <public-ip>:443 -servername <public-dns-name> </dev/null 2>/dev/null \
  | openssl x509 -noout -fingerprint -sha256
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/ssl/kinto/kinto.crt kinto.crt
curl --cacert kinto.crt -u admin https://<public-dns-name>/v1/

curl -u admin asks for the password, so it does not end up in your shell history.

Step 6: Store and read documents

Create a bucket and a collection, add a record and read it back (replace <KINTO_ADMIN_PASSWORD> with the password from Step 4):

K="curl -s --cacert /etc/ssl/kinto/kinto.crt -u admin:<KINTO_ADMIN_PASSWORD>"
$K -o /dev/null -w 'bucket %{http_code}\n' -X PUT https://localhost/v1/buckets/demo
$K -o /dev/null -w 'collection %{http_code}\n' -X PUT https://localhost/v1/buckets/demo/collections/notes
$K -H 'Content-Type: application/json' -X POST https://localhost/v1/buckets/demo/collections/notes/records \
  -d '{"data": {"title": "hello kinto", "done": false}}' | python3 -m json.tool
$K https://localhost/v1/buckets/demo/collections/notes/records | python3 -m json.tool
bucket 201
collection 201
{
    "data": {
        "title": "hello kinto",
        "done": false,
        "id": "2b0b4a6c-...",
        "last_modified": 1793400000000
    },
    "permissions": {
        "write": [
            "account:admin"
        ]
    }
}

An authenticated round trip: bucket and collection created, a record stored and read back

Clients that synchronise ask only for what changed since their last sync, for example ?_since=<last_modified>; see the Kinto API documentation for filtering, sorting, pagination, groups and permissions. To remove the demo data:

curl -s --cacert /etc/ssl/kinto/kinto.crt -u admin:<KINTO_ADMIN_PASSWORD> -o /dev/null -w '%{http_code}\n' -X DELETE https://localhost/v1/buckets/demo

Step 7: See what is refused

Anonymous visitors can read the public hello and nothing else; they cannot write, cannot create an account and cannot read your records:

curl -s --cacert /etc/ssl/kinto/kinto.crt -o /dev/null -w 'anonymous write %{http_code}\n' -X PUT https://localhost/v1/buckets/anon
curl -s --cacert /etc/ssl/kinto/kinto.crt -o /dev/null -w 'anonymous sign up %{http_code}\n' -H 'Content-Type: application/json' \
  -X PUT https://localhost/v1/accounts/intruder -d '{"data": {"password": "intruder-password"}}'
curl -s --cacert /etc/ssl/kinto/kinto.crt -o /dev/null -w 'admin:admin %{http_code}\n' -u admin:admin https://localhost/v1/buckets
anonymous write 401
anonymous sign up 401
admin:admin 401

The rules are in /etc/kinto/kinto.ini: every *_principals setting lists account:admin only. To let your own users sign up, add system.Everyone to kinto.account_create_principals (each new account can then write only what it is given permission on) and restart Kinto with sudo systemctl restart kinto.

Secure by default: only account:admin may create or write; anonymous write, anonymous sign up and admin:admin are refused

Step 8: Use your own host name, certificate and web app origin

Point a DNS record at the instance (an Elastic IP keeps the address across stop and start), then tell the instance its name. This mints a new self signed certificate for that name, updates the nginx host rule, Kinto's kinto.http_host and CORS origins and the address and fingerprint in the credentials file, and keeps the name across reboots:

sudo kinto-set-hostname kinto.example.com

To use a certificate from a public authority instead, replace /etc/ssl/kinto/kinto.crt (full chain) and /etc/ssl/kinto/kinto.key (mode 0600, owned by root) with your own and run sudo systemctl reload nginx. Running kinto-set-hostname again later would replace them with a new self signed pair.

If a browser application on another origin calls the API, add that origin (space separated) to kinto.cors_origins in /etc/kinto/kinto.ini, for example kinto.cors_origins = https://kinto.example.com https://app.example.com, then run sudo systemctl restart kinto. kinto-set-hostname keeps the origins you add.

Step 9: Change the password, back up and update

Change the administrator password (it is read from the terminal or standard input, never the command line, and the old password stops working at once), then update or remove the line in the credentials file:

sudo kinto-set-admin-password admin

Every service is enabled, so Kinto returns after a reboot or a stop and start with your data intact. Unattended security upgrades keep the operating system, PostgreSQL and nginx patched:

systemctl is-enabled kinto nginx unattended-upgrades
enabled
enabled
enabled

Your data lives in PostgreSQL in /var/lib/postgresql/16/main on the root volume. Back it up with a dump (or an EBS snapshot / AWS Backup of the instance):

sudo -u postgres pg_dump -Fc kinto > kinto-$(date +%F).dump

PostgreSQL has no network listener. If another host must reach it, add listen_addresses and a pg_hba.conf rule with a password role yourself, and open the port only to that host. cloudimg publishes a new version of the image when a newer Kinto release is available.

Security notes

  • No shared secret ships in the image. No database, account, password, password hash, HMAC secret, TLS key or credentials file exists until your instance's first boot generates them, and nginx refuses to start until that has finished.
  • Fail closed. If first boot cannot complete, the HTTPS port is closed again and nothing is served.
  • Kinto is confined. It runs as the unprivileged kinto account with no writable path at all (its code is owned by root and read only to it, and every byte of data is in PostgreSQL), cannot read the credentials file or the TLS key, and is blocked from the EC2 instance metadata service, as are nginx and PostgreSQL.
  • The host and client address come from configuration and the connection, never from request headers, so a forged Host or X-Forwarded-For header changes nothing.
  • No plugin that would widen the surface. The image enables only the accounts plugin: no anonymous default bucket, no OpenID login, no flush endpoint and no admin web UI.
  • Known residual findings, not reachable here. Kinto 26.5.0 requires setuptools older than 82, and setuptools 81.0.0 has a moderate (GHSA-h35f-9h28-mq5c) and a low (GHSA-xmj3-9gf7-h52w) advisory; both affect building source packages or installing legacy packages on Windows, which this image never does. The PostgreSQL driver's wheel bundles its own OpenSSL 3.5.8 and libpq; Kinto connects over the local socket, so their network code is unused.
  • Restrict 22/tcp to your management network, and 80/443 to the networks that use the API.

Architecture summary

Component Detail
Document store Kinto 26.5.0 (Apache-2.0), venv /opt/kinto/venv (root owned, every wheel pinned by SHA-256)
Application server waitress (via pserve) on 127.0.0.1:8888, unit kinto.service, account kinto
Settings /etc/kinto/kinto.ini, mode 0640 root:kinto (HMAC secret, host, CORS origins, permissions)
Database PostgreSQL 16, local socket only, peer authentication, database and role kinto
Web server nginx on 443 (HTTPS, per instance certificate in /etc/ssl/kinto), 80 redirects to HTTPS
Sign in Kinto accounts (HTTP Basic), bcrypt hashes in PostgreSQL; administrator admin
Credentials /root/kinto-credentials.txt, mode 0600, root only
First boot kinto-firstboot.service, log /var/log/cloudimg/kinto-firstboot.log
Tools kinto-set-admin-password, kinto-set-hostname, kinto-verify.sh
Ports 22 SSH, 80 redirect, 443 Kinto API
Base image Ubuntu 24.04 LTS, fully patched, unattended security upgrades enabled, IMDSv2 required

Support

cloudimg images come with 24/7 support. Email support@cloudimg.co.uk with your AWS account ID and the instance ID. For Kinto itself, see the Kinto documentation and the Kinto project on GitHub.

Kinto is an open source project originally created by Mozilla. cloudimg is not affiliated with or endorsed by Mozilla or the Kinto project.