Storage AWS

Sharry 1.17.0 on AWS User Guide

| Product: Sharry 1.17.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Sharry 1.17.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.

Sharry is a self hosted file sharing application. You sign in, upload one or many files into a share, and publish it: the recipient gets a link (optionally protected by a password, limited in time and in the number of views) and downloads the files in the browser, one by one or as a ZIP. The other direction works too: an alias page is an upload page you hand to someone else, so they can send files to you without an account.

The cloudimg image locks Sharry down:

  • No default administrator and no sign up. The image ships no database, no settings file and no password. First boot creates the admin account with a password unique to your instance and writes it to /root/sharry-credentials.txt (root only). Self sign up is closed (upstream's default is open); you create accounts for other people yourself.
  • Per instance secrets. Sharry's session signing secret (upstream ships the well known value hex:caffee), the administrator password, the PostgreSQL password, the invite password and the TLS key are all generated on your instance's first boot. None of them exists in the image, and none is ever passed on a command line.
  • HTTPS from the first boot. nginx serves Sharry 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. Sharry listens on 127.0.0.1:9090 only, as the unprivileged sharry account; PostgreSQL 16 listens on 127.0.0.1:5432 only. A certificate you install yourself is never replaced (Step 9).
  • Current library fixes. The release's web server library (http4s) is raised to 0.23.37 and the other affected Java libraries to their fixed releases, so no critical advisory ships (see Security notes).
  • Hardened at the proxy. nginx refuses request smuggling shapes before they reach Sharry, checks the caller's sign in before it reads an upload, caps each upload request, serves files people upload as sandboxed downloads, and closes the two upstream issues that have no release yet (a stored script injection, and the alias takeover in issue #1848). Each is described under Security notes, with what it changes for you.
  • No third party services. The web application loads nothing from other sites, the API documentation page does not call the online Swagger validator, mail and the S3 store are off, and Sharry, nginx and PostgreSQL cannot reach the EC2 instance metadata service.
  • Your data on its own volume. Uploaded files and the database live on a dedicated 20 GiB data volume that you can grow and snapshot on its own.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Sharry 1.17.0 (GNU GPL 3.0 or later), the unmodified upstream release with 51 of its Java libraries replaced by fixed releases of the same libraries; OpenJDK 21 (headless), PostgreSQL 16, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. The licence and the complete source of the release are on the image in /usr/share/doc/sharry (see Source code and licence).

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 (Sharry) and TCP 80 (redirects to HTTPS) from your own networks only. Open 443 more widely only to the people who should be able to download shares or use your upload pages.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Sharry 1.17.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.

The AMI has two volumes: the 8 GiB root volume (/dev/sda1) and a 20 GiB data volume (/dev/sdf, mounted at /var/lib/sharry) that holds every uploaded file and the database. If you will share large files, raise the data volume under Configure storage before you launch; Step 10 shows how to grow it later.

With the AWS CLI (replace the values in angle brackets; this sets the data volume to 100 GiB):

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 \
  --block-device-mappings 'DeviceName=/dev/sdf,Ebs={VolumeSize=100,VolumeType=gp3}' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=sharry}]'

Step 2: Connect to your instance

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

Step 3: Check the services

First boot creates the PostgreSQL cluster on the data volume, generates the secrets and the certificate, works out the instance's name, starts Sharry, proves a signed in round trip and only then opens the web server. It takes under a minute:

systemctl is-active sharry-set-host sharry nginx postgresql@16-main
active
active
active
active

sharry-set-host runs on every boot before nginx and Sharry: it keeps the served name, the certificate and Sharry's address in step (Step 9).

Only SSH (22) and nginx (80, 443) listen on the network. Sharry (9090) and PostgreSQL (5432) listen on the loopback address only; the two :53 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:5432
127.0.0.1:9090
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80

If nginx is not running, first boot has not finished, or it failed, in which case the site is never opened. Check systemctl status sharry-firstboot and sudo tail -40 /var/log/cloudimg/sharry-firstboot.log.

Step 4: Get your URL and password

sudo cat /root/sharry-credentials.txt

The file is readable by root only and holds the address, this instance's administrator sign in and the certificate fingerprint:

# Sharry 1.17.0 - per-instance credentials, generated on first boot (cloudimg). Keep this file secret (0600 root).
# Sign in at SHARRY_URL as SHARRY_ADMIN_USER. Change the password: sudo sharry-set-admin-password
# Self sign-up is closed: create accounts under Administration > Accounts. SHARRY_INVITE_PASSWORD is only needed if
# you switch sign-up to "invite" mode (see the guide).
# Certificate: self-signed for this instance; compare SHARRY_TLS_SHA256 with what your browser shows, or replace it with
# a CA-issued certificate (see the guide).
SHARRY_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
SHARRY_ADMIN_USER=admin
SHARRY_ADMIN_PASSWORD=<SHARRY_ADMIN_PASSWORD>
SHARRY_INVITE_PASSWORD=<SHARRY_INVITE_PASSWORD>
SHARRY_TLS_SHA256=A8:B9:C9:8C:6D:89:4E:D8:BD:01:77:ED:8C:46:CB:AA:43:97:A5:76:C1:15:50:01:E5:1D:19:DB:6A:77:1C:FD

To see only the address and the fingerprint (no password on screen):

sudo grep -E '^SHARRY_(URL|ADMIN_USER|TLS_SHA256)=' /root/sharry-credentials.txt
SHARRY_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
SHARRY_ADMIN_USER=admin
SHARRY_TLS_SHA256=A8:B9:C9:8C:6D:89:4E:D8:BD:01:77:ED:8C:46:CB:AA:43:97:A5:76:C1:15:50:01:E5:1D:19:DB:6A:77:1C:FD

You can confirm the whole sign in path from the instance itself. The helper drives the real API through nginx: the version and the app page with its script nonce, that a wrong password, upstream's default admin:admin, a cross site sign in, self sign up and a forged proxy user are refused, that the administrator signs in, and a full upload, publish and download round trip (deleted again afterwards):

sudo /usr/local/sbin/sharry-verify.sh
SHARRY_ROUNDTRIP_OK host=ec2-203-0-113-10.compute-1.amazonaws.com via=172.31.16.10

To change the administrator password later, run sudo sharry-set-admin-password (it asks twice, or reads one line on standard input) and Sharry restarts with it.

Step 5: Sign in

Open the SHARRY_URL address in your browser. The certificate is self signed (see Step 9), so the browser asks you to accept it once; compare the fingerprint it shows with the one on the instance:

sudo openssl x509 -in /etc/ssl/sharry/sharry.crt -noout -fingerprint -sha256
sha256 Fingerprint=A8:B9:C9:8C:6D:89:4E:D8:BD:01:77:ED:8C:46:CB:AA:43:97:A5:76:C1:15:50:01:E5:1D:19:DB:6A:77:1C:FD

Sign in as admin with the password from Step 4. The page offers no sign up, because self sign up is closed:

The Sharry sign in page

Step 6: Share files

Choose New Share (or Create a Share from the start page), drop your files or use Select Files, and optionally give the share a description (Markdown) and a name. Further down the form you can also set how long the share stays valid, how many times it may be viewed (Max. Views) and a password. Then choose Submit:

Two files, a description and a name in the Create a Share form

The browser sends each file in chunks of 10 MiB (resumable: Pause and a dropped connection pick up where they stopped), so a single file can be up to 1.5 GiB, the share limit shown on the form. When the upload finishes, open the share and choose Publish. Share Link then shows the public link, a QR code and a Copy Link button:

The published share with its public link

The recipient opens the link and sees the description and the files, and can download them one by one or as a ZIP. The page itself needs no account:

What the recipient sees: the public share page

Send people the share page link (https://<your-host>/app/open/<id>) exactly as Share Link shows it. A download link copied out of that page and opened directly from another site, an email client or the address bar is refused (403), because opening a share counts a view toward its limit and the image only accepts that from Sharry's own pages; downloads started on the share page work normally.

Your Shares lists everything you have shared, whether it is published, its view limit, the number of files and the size; open a share to unpublish it, add files, change its details or delete it:

Your Shares

The upload limits are in Sharry's settings file (root owned, readable by the sharry account only):

sudo grep -nE "^\s*(max-size|max-validity|zip-max-size|chunk-size|mode) = " /etc/sharry/sharry.conf
18:    chunk-size = "10M"
65:      mode = "closed"
70:      chunk-size = "512K"
71:      max-size = "1.5G"
72:      max-validity = 365 days
73:      zip-max-size = "1.5G"

Line 18 is the browser's upload chunk, line 65 the sign up mode, and lines 71 to 73 the largest share, the longest validity and the largest ZIP download. To allow bigger shares, raise max-size (and zip-max-size), make sure the data volume has room (Step 10), and restart Sharry with sudo systemctl restart sharry. If you raise the browser's chunk-size (line 18), keep it below the 11 MiB per request cap nginx applies to uploads (see Uploads under Security notes), or raise that cap in /etc/nginx/snippets/sharry-upload.conf with it.

Step 7: Receive files with an alias page

An alias page is an upload page for other people: they open its link and send files to you, and each upload arrives as a share in your account. Choose Alias Pages, then New Alias Page, give it a name and a validity for the uploads, and save it. Send the alias page's link to the people who should upload; disable or delete it when you no longer want uploads:

An upload alias page

Editing an existing alias is disabled on this image. Because of upstream issue #1848 (any signed in account can add itself to another account's alias by editing it, and then read and delete the shares uploaded through it; no fix has been released), nginx refuses the one request that saves changes to an existing alias. You can still create, list, open and delete aliases. To change an alias's name, validity, members or whether it is enabled, create a new alias with the settings you want and delete the old one; the new alias has a new upload link, so send that link out again. Details, and how to lift the restriction if every account on your server is trusted, are under Alias editing (issue #1848) in the Security notes.

Step 8: Accounts

The image has one account, admin. Create an account for each person: open the menu at the top right and choose Accounts (administrators only), then add the account. Each person then signs in with their own name and password and sees only their own shares. Sign up stays closed. If you would rather let people register themselves with a shared code, set mode = "invite" on line 65 of /etc/sharry/sharry.conf and restart Sharry: registering then needs an invite key, and invite keys are generated with the SHARRY_INVITE_PASSWORD from the credentials file (see the invites section of the Sharry documentation).

Step 9: Your own host name and certificate

Point a DNS record at the instance (an Elastic IP keeps the address across stop and start), then tell the instance its name. This updates the nginx host rule, the cross site guard, Sharry's own address (used in share links) and the address in the credentials file, and keeps the name across reboots. While the instance still uses the certificate it generated itself, it also generates a new self signed certificate for the name:

sudo sharry-set-hostname files.example.com

Which certificate is in use. The image records the SHA-256 fingerprint of every certificate it generates in /var/lib/cloudimg/sharry-cert.sha256 (owned by root). Only a certificate whose fingerprint matches that record is ever replaced, and only when the name changes. Any other certificate in /etc/ssl/sharry, including a symbolic link to a certificate kept elsewhere, is treated as yours and kept exactly as it is on every reboot and every name change. To see which one the instance is serving:

[ "$(sudo head -n1 /var/lib/cloudimg/sharry-cert.sha256)" = "$(sudo openssl x509 -in /etc/ssl/sharry/sharry.crt -noout -fingerprint -sha256 | cut -d= -f2)" ] && echo "self-signed certificate minted by this image (replaced when the host name changes)" || echo "your own certificate (kept unchanged on every boot and host name change)"
self-signed certificate minted by this image (replaced when the host name changes)

Install your own certificate. Copy your certificate (with its intermediate certificates, leaf first) and its private key over the two files, keep the key readable by root only, and reload nginx:

sudo cp fullchain.pem /etc/ssl/sharry/sharry.crt
sudo cp privkey.pem /etc/ssl/sharry/sharry.key
sudo chown root:root /etc/ssl/sharry/sharry.crt /etc/ssl/sharry/sharry.key
sudo chmod 0644 /etc/ssl/sharry/sharry.crt
sudo chmod 0600 /etc/ssl/sharry/sharry.key
sudo nginx -t && sudo systemctl reload nginx

You can instead link the two files to a certificate kept elsewhere (for example sudo ln -sf /etc/letsencrypt/live/files.example.com/fullchain.pem /etc/ssl/sharry/sharry.crt and the same for privkey.pem to sharry.key). cloudimg tests both forms on the build instance: a copied pair through a real reboot, and a copied pair and a linked chain through the every boot step and a host name change. Each time the certificate and key are byte for byte the same afterwards and nginx serves them. If you later run sharry-set-hostname with a new name, your certificate is still kept (the command says the certificate in /etc/ssl/sharry is not one this helper minted: it is kept unchanged), so make sure it names the new host. Renewing is the same copy and reload.

Go back to a self signed certificate. Remove your two files and run the helper again; it generates a new certificate, records its fingerprint, writes it into the credentials file and reloads nginx:

sudo rm /etc/ssl/sharry/sharry.crt /etc/ssl/sharry/sharry.key
sudo sharry-set-hostname files.example.com
sharry-set-hostname: Sharry is served at https://files.example.com/ (certificate SHA-256 <the new certificate's fingerprint>)

If you never set a name, use sudo sharry-set-hostname --boot instead, which keeps the instance's EC2 name. Alternatively, put a load balancer or reverse proxy that holds a publicly trusted certificate (for example an AWS Certificate Manager certificate) in front of the instance and forward to it on 443 with the same host name.

Step 10: Data volume, PostgreSQL, backups and updates

Everything Sharry stores is on the data volume mounted at /var/lib/sharry: the uploaded files in files (owned by sharry), the PostgreSQL 16 cluster in postgresql (owned by postgres), and nginx's temporary upload buffer in nginx-body. Each folder is readable by its own account only:

df -h /var/lib/sharry
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme2n1     20G   60K   19G   1% /var/lib/sharry

To grow it, modify the EBS volume in the EC2 console (or aws ec2 modify-volume), then grow the file system online with sudo resize2fs on the device df shows.

Sharry keeps its accounts, shares and alias pages in the sharry database. PostgreSQL listens on 127.0.0.1 only and accepts exactly one network sign in: the sharry role, with its per instance password, from the loopback address:

sudo -u postgres psql -X -tA -c "SHOW listen_addresses"
127.0.0.1

To back up, take an EBS snapshot of the data volume (or use AWS Backup on the instance), or dump the database and copy the files folder:

sudo -u postgres pg_dump -Fc sharry | sudo tee /root/sharry-db.dump > /dev/null
sudo tar -C /var/lib/sharry -czf /root/sharry-files.tar.gz files

Every service starts again after a reboot or a stop and start, with your accounts and shares as they were. sharry-firstboot ran once and is disabled; PostgreSQL's cluster unit is enabled through postgresql.service (enabled-runtime):

systemctl is-enabled sharry-set-host sharry-firstboot sharry nginx postgresql@16-main unattended-upgrades
enabled
disabled
enabled
enabled
enabled-runtime
enabled

Updates. Unattended security upgrades keep Ubuntu, nginx, PostgreSQL 16 and OpenJDK 21 patched from the Ubuntu archive:

dpkg-query -W -f='${Package} ${Version}\n' nginx postgresql-16 openjdk-21-jre-headless
nginx 1.24.0-2ubuntu7.19
openjdk-21-jre-headless 21.0.12.1+1-1~24.04.4
postgresql-16 16.15-0ubuntu0.24.04.1

Sharry itself is not an Ubuntu package: it lives in /opt/sharry (owned by root, read only) with the library versions cloudimg pinned, and apt never changes it. cloudimg publishes a new version of the image when a new Sharry release (or a security fix for one of its libraries) is available; to move to it, launch the new version and restore your database dump and files folder, or attach a snapshot of your data volume.

Security notes

Secrets. The credentials file and the TLS key are readable by root only. /etc/sharry/sharry.conf, which holds the session signing secret, the administrator password and the database password, is owned by root and readable by the sharry account only. The Sharry service runs as sharry with a read only system (ProtectSystem=strict), no new privileges, and write access to its file store only.

Libraries (http4s 0.23.37 and the other raised jars). The Sharry 1.17.0 release bundles http4s 0.23.30, whose server and HTTP parsing code has published request smuggling, HTTP/2 and path handling advisories, plus older releases of the JSON parser (jawn), the PostgreSQL driver, Jackson, Bouncy Castle, Tika, Guava and others with their own advisories. cloudimg replaced 51 of the release's jars with the fixed releases of the same libraries (http4s 0.23.37 with the Cats Effect 3.7, fs2 3.14 and circe 0.14.16 releases it needs, jawn 1.7.0, PostgreSQL driver 42.7.14, Tika 3.2.2, Guava 33.7.2 and more), each pinned by its SHA-256, and runs the release's own classes on top unchanged. The list is in /usr/share/doc/cloudimg/sharry-jar-swaps.tsv:

ls /opt/sharry/lib | grep -E "^(http4s-(core|ember-server|server)|cats-effect_|fs2-io|jawn-parser|postgresql|tika-core)"
cats-effect_3-3.7.1.jar
fs2-io_3-3.14.0.jar
http4s-core_3-0.23.37.jar
http4s-ember-server_3-0.23.37.jar
http4s-server_3-0.23.37.jar
jawn-parser_3-1.7.0.jar
postgresql-42.7.14.jar
tika-core-3.2.2.jar

What remains is in the MinIO S3 client and its compression library, which only the S3 store uses, and that store is switched off (see S3 store below).

Request smuggling. nginx is the only server on the network, and it re-frames every request before Sharry sees it: it speaks HTTP/1.1 to Sharry with the Connection header cleared, reads each request body itself and sends it on with its own Content-Length (buffering is never turned off; large bodies are buffered on the data volume, not the root disk). A request that carries both Content-Length and Transfer-Encoding, or a transfer encoding other than chunked, is refused before anything is forwarded:

H=$(sed -n 's/^set \$sharry_host "\(.*\)";$/\1/p' /etc/nginx/sharry-host.conf); printf "POST /api/v2/open/info/version HTTP/1.1\r\nHost: %s\r\nContent-Length: 5\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n0\r\n\r\nX" "$H" | timeout 10 openssl s_client -quiet -servername "$H" -connect 127.0.0.1:443 2>/dev/null | head -1
HTTP/1.1 400 Bad Request

Uploads. The two upload routes (your own uploads and alias page uploads) check the caller's sign in, or the alias id, before nginx reads a single byte of the body: nginx asks Sharry with the caller's own credentials and no body, and refuses anything Sharry does not accept. An unauthenticated upload is therefore refused without being stored anywhere, whatever its size:

H=$(sed -n 's/^set \$sharry_host "\(.*\)";$/\1/p' /etc/nginx/sharry-host.conf); head -c 2000000 /dev/zero | curl -s -o /dev/null -w "%{http_code}\n" --resolve "$H:443:127.0.0.1" --cacert /etc/ssl/sharry/sharry.crt -X POST -H "Origin: https://$H" -H "Content-Type: application/octet-stream" --data-binary @- "https://$H/api/v2/sec/upload"
403

Each upload request is capped at 11 MiB and each client address is rate and connection limited:

grep -E "^(client_max_body_size|limit_req |limit_conn )" /etc/nginx/snippets/sharry-upload.conf
client_max_body_size 11m;
limit_req zone=sharry_upload_req burst=60 nodelay;
limit_conn sharry_upload_conn 10;

The web app is unaffected, because it uploads in 10 MiB chunks (the resumable tus protocol) and a file can be up to the share limit across its chunks. What the cap changes: a single multipart upload through the API (one request carrying a whole file, as some scripts do) larger than 11 MiB is refused with 413; send bigger files through the web app or with the tus protocol in chunks of at most 10 MiB. If you raise Sharry's chunk-size (Step 6), the cap must stay above it. Other API requests are capped at 16 MiB.

Files people upload are served as sandboxed downloads. Every file Sharry returns carries Content-Security-Policy: sandbox (no scripts, no forms, no same origin access) and X-Content-Type-Options: nosniff. Images (PNG, JPEG, GIF, WebP, AVIF, BMP), video, audio, PDF and plain text still open in the browser; HTML, SVG, XML, JavaScript and any other type is sent as a download (Content-Disposition: attachment) instead of being opened, so an uploaded web page or SVG with a script never runs on your Sharry's address. cloudimg checks this in a real browser on the build instance: an uploaded HTML file and an SVG file with scripts download and never execute, directly or on the share page.

grep -n "Content-Security-Policy \"sandbox" /etc/nginx/snippets/sharry-file-headers.conf
10:add_header Content-Security-Policy "sandbox; default-src 'none'; img-src 'self' data:; media-src 'self'; style-src 'unsafe-inline'; frame-ancestors 'self'" always;

Script injection in share descriptions (fixed upstream after 1.17.0). A javascript: link in a share description could run in the viewer's browser in Sharry 1.17.0; upstream's fix (pull request #1842) came after the 1.17.0 release and is not in a release yet. On this image every Sharry page carries a Content Security Policy that only allows Sharry's own scripts, marked with a nonce that changes on every request, so such a link, or any other injected script, does nothing:

H=$(sed -n 's/^set \$sharry_host "\(.*\)";$/\1/p' /etc/nginx/sharry-host.conf); curl -s -D - -o /dev/null --resolve "$H:443:127.0.0.1" --cacert /etc/ssl/sharry/sharry.crt "https://$H/app/login" | grep -i "^content-security-policy" | cut -c1-110
content-security-policy: default-src 'self'; script-src 'self' 'nonce-1f22e950c8aad8f2ab6136313f9911d3'; st

Alias editing (issue #1848). In Sharry 1.17.0 the request that saves an existing alias does not check that the alias belongs to the caller, so any signed in account could add itself as a member of someone else's alias and then read and delete the files uploaded through it. No upstream fix is released yet. nginx refuses that request, POST /api/v2/sec/alias/<id>, with 403, in every spelling of the path (upper case, repeated or trailing slashes; encoded separators are refused with 400). What you lose: saving changes to an existing alias (its name, validity, members, and enabling or disabling it). Creating, listing, opening and deleting aliases, and uploading through them, work as before. To change an alias, create a new one and delete the old one (Step 7). The rule is one line:

cat /etc/nginx/snippets/sharry-alias-guard.conf
# cloudimg (Sharry): refuse in-place alias edits until upstream fixes issue #1848 (an account could add itself to
# another account's alias). Create a new alias and delete the old one instead. If EVERY account on this server is
# trusted, you may empty this file and run: sudo nginx -t && sudo systemctl reload nginx
if ($sharry_alias_modify) { return 403; }

If you are the only user, or every account belongs to people you trust with each other's uploads, you may empty that file and reload nginx to edit aliases in place. cloudimg will publish a new image version without the rule once upstream releases a fix.

Cross site requests. A request that changes something (POST, PUT, PATCH, DELETE) is refused unless it comes from Sharry's own address, so another website cannot use a signed in user's browser to act on Sharry. Opening a share through the API counts a view, so it is accepted only from Sharry's own pages (Step 6). Scripts that use the API work when they send no Origin header, or send Sec-Fetch-Site: same-origin for the share routes. Raw paths with encoded /, ., \ or dot segments are refused with 400.

S3 store. Sharry can keep files in an S3 compatible store through the MinIO client. That store is switched off on this image, because the bundled MinIO client still has one high severity advisory (CVE-2025-59952) that only a major library upgrade fixes; with the store off, that code is never used. Files go to the data volume:

sudo grep -n "minio = " /etc/sharry/sharry.conf
59:        minio = { enabled = false, type = "s3" }

No third party calls by default. The web app loads its scripts, styles, fonts and flags from the instance itself; the API documentation page (/api/doc) has Swagger UI's online validator switched off; mail (and therefore share notifications by email) and the OAuth, HTTP, command and proxy sign in modules are off. cloudimg checks this in a real browser on the build instance: the sign in page, the start page, the upload form, the share and alias pages, a public share page and the API documentation make 0 requests to any other host.

Instance metadata. Sharry, nginx and PostgreSQL cannot reach the EC2 instance metadata service, so nothing they process can be used to read the instance's role credentials (IMDSv2 is also required for the instance itself):

for u in sharry nginx postgresql@; do grep -H IPAddressDeny /etc/systemd/system/$u.service.d/50-cloudimg-imds-deny.conf; done
/etc/systemd/system/sharry.service.d/50-cloudimg-imds-deny.conf:IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
/etc/systemd/system/nginx.service.d/50-cloudimg-imds-deny.conf:IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
/etc/systemd/system/postgresql@.service.d/50-cloudimg-imds-deny.conf:IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128

Client addresses in nginx's and Sharry's logs come from the connection itself; a forged X-Forwarded-For header is overwritten.

Source code and licence

Sharry is free software under the GNU General Public License 3.0 or later. The image ships the licence and the complete source of the 1.17.0 release (the tag's source archive) next to the program, and the build record lists the exact release, source and library digests:

ls /usr/share/doc/sharry
LICENSE.txt
sharry-1.17.0-src.tar.gz
sharry.conf.release-sample
head -3 /usr/share/doc/cloudimg/sharry-BUILD-INFO.txt
Sharry 1.17.0 on Ubuntu 24.04 (cloudimg AWS bundle member storage)
release https://github.com/eikek/sharry/releases/download/v1.17.0/sharry-restserver-1.17.0.zip sha256 2b70f17a95416678c2075aec5422d168cb4d091a959b0f17cf48dbb8f2e07ed5 (tag v1.17.0 = 31154b4ebab6e0c623a9ccb7b54691d7b8d6eb80)
source https://github.com/eikek/sharry/archive/refs/tags/v1.17.0.tar.gz sha256 c3656470bb631c291ff9a41257dfdee13d5e838e1625c23eaeb4c4ba7e9a3ab2 -> /usr/share/doc/sharry/

The replaced libraries are the unmodified releases from Maven Central (Apache 2.0, MIT and BSD licences, as the originals); their names, versions and SHA-256 digests are in /usr/share/doc/cloudimg/sharry-jar-swaps.tsv.

Support

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

Sharry is an open source project by Eike Kettner and contributors. cloudimg is not affiliated with or endorsed by the Sharry project.