Applications AWS

Dicoogle 3.6.0 on AWS User Guide

| Product: Dicoogle 3.6.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Dicoogle 3.6.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg GIS, health and research applications listing on AWS Marketplace; you choose it as the software version when you launch.

Dicoogle is an open source Picture Archiving and Communication System (PACS) from the University of Aveiro. It receives medical images over DICOM (C-STORE), indexes their metadata with Lucene so you can search every DICOM tag, serves them to viewers and other PACS (C-FIND, C-MOVE, WADO), and has a web interface for search, preview, export and administration.

The cloudimg image is a hardened, ready-to-use Dicoogle server:

  • HTTPS from the first boot, with a certificate generated on your instance (an end-entity certificate, not a certificate authority), TLS 1.2 and 1.3 only. Port 80 only redirects to HTTPS, so the sign-in and the session never cross the network in clear text. Requests for an unknown host name are dropped.
  • nginx in front, Dicoogle behind it. nginx is the only public web listener. Dicoogle's own web server listens on 127.0.0.1:18080 and is also refused from other hosts by the instance's firewall. nginx sets the client address Dicoogle sees from the real connection (a forged X-Forwarded-For is overwritten), limits sign-in attempts per client address, refuses a password in a query string, and adds HSTS, a Content Security Policy, Referrer-Policy: same-origin and X-Frame-Options: DENY.
  • A per-instance administrator. First boot generates the password of the dicoogle administrator and writes it to a root-only file. Dicoogle's built-in dicoogle/dicoogle account is never created: Dicoogle cannot start until first boot has written this instance's own user file. Every REST call and every page of the web UI needs a signed-in session.
  • DICOM closed until you open it. The storage port (6666) and the query/retrieve port (1045) are not in the security group and are refused by the instance's DICOM peer allow-list, which ships empty. You allow a modality or PACS by its address in both places (Step 6).
  • A random calling AE title. First boot sets one random calling AE title as the only one Dicoogle accepts for C-STORE and C-MOVE; a sender with any other AE title is refused. You add your modalities' AE titles with one command.
  • An outbound block. C-MOVE sends images to an address you configure, so the dicoogle account (and nginx) are refused, by iptables and by systemd, every connection to the EC2 metadata service, to private and reserved ranges and to this instance's own VPC ranges. A fresh instance cannot be turned into a scanner of your network. You allow a real PACS on a private address explicitly (Step 7).
  • An empty archive. The image ships with an empty index and an empty archive: no study, no patient data and no test image. The build stored and deleted synthetic objects only, and checked that none remained.
  • An unprivileged service. Dicoogle runs as the dicoogle account, which has no shell, no password and no sudo rights, under systemd with NoNewPrivileges, a read-only operating system (ProtectSystem=strict), no capabilities and core dumps off. The Java heap is half of the instance's memory (1 to 8 GiB) and Dicoogle restarts if it ever runs out.
  • Pinned, unmodified upstream. The Dicoogle 3.6.0 release and its two plugins are checked against pinned SHA-256 hashes at build time and again at every start. OpenJDK 17 and nginx come from the Ubuntu 24.04 main archive.

What is on the image: Dicoogle 3.6.0 (the Lucene index and file storage plugins) under OpenJDK 17, nginx and iptables from Ubuntu 24.04, 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 (the web UI and REST API) and TCP 80 (redirects to HTTPS) from the networks your users are on. Do not open 18080: Dicoogle's web server listens on the loopback address only. Open TCP 6666 and TCP 1045 only to the exact addresses of your modalities and PACS (a /32 each), when you connect them (Step 6).
  • IMDSv2 enabled (the default for this AMI). The instance reads its own VPC ranges from the metadata service at every boot; with the metadata service switched off, Dicoogle does not start (see Step 10).
  • For production, a DNS name you control (for example pacs.example.com) and, ideally, a certificate from a certificate authority. Until then the instance uses its public IP address with its own self-signed certificate.
  • Medical images are sensitive data. Run the instance in a private subnet or behind a VPN where you can, keep 443 open only to your clinical networks, and follow your organisation's rules for patient data.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Dicoogle 3.6.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger for big archives or many concurrent users), your key pair, and a security group as above. The archive and its index live on the root volume under /var/lib/dicoogle; choose a root volume large enough for your images, or add a data volume (Step 11).

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --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=dicoogle}]'

First boot takes about a minute after the instance is running: it generates the administrator password, the calling AE title and the certificate, then Dicoogle starts. First boot uses the instance's public IP address as its name (or its private address if it has no public one), and also serves its EC2 public DNS name and its private address.

Step 2: Connect to your instance

Operating system SSH user
Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

The login message shows the web address and where the password is.

Step 3: Check the services

systemctl is-active cloudimg-dicoogle-vpc-cidrs cloudimg-dicoogle-egress dicoogle nginx
active
active
active
active

(dicoogle-firstboot runs once, on the first boot, and shows inactive on every later boot.)

The listeners: SSH, HTTP and HTTPS for everyone, Dicoogle's web server on the loopback address, and the two DICOM ports, which the firewall refuses to every address that is not a listed peer:

sudo ss -Htln | awk '{print $4}' | sort -u
0.0.0.0:1045
0.0.0.0:22
0.0.0.0:443
0.0.0.0:6666
0.0.0.0:80
127.0.0.1:18080
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80

(127.0.0.53 and 127.0.0.54 are Ubuntu's local DNS resolver.)

Step 4: Read the address, the password and the certificate fingerprint

Everything first boot generated is in /root/dicoogle-credentials.txt (root only). The web address, the host name and the certificate's SHA-256 fingerprint:

sudo grep -E '^DICOOGLE_(URL|HOST|TLS_SHA256)=' /root/dicoogle-credentials.txt
DICOOGLE_URL=https://203.0.113.10/
DICOOGLE_HOST=203.0.113.10
DICOOGLE_TLS_SHA256=3E:09:D1:00:B7:E2:8B:75:92:EA:0A:DF:C9:E7:F3:9E:0C:23:98:A7:D4:A5:83:89:D3:B8:81:B0:6D:DD:33:C0

The same file holds DICOOGLE_ADMIN_PASSWORD (the administrator is DICOOGLE_ADMIN_USER, dicoogle) and the DICOM settings of Step 6. Read it with sudo cat /root/dicoogle-credentials.txt when you need it, and keep the password out of tickets and chat. The certificate is self-signed for this instance: when a browser asks you to trust it, compare its SHA-256 fingerprint with DICOOGLE_TLS_SHA256.

A quick check from the instance itself (/healthz is the only page that answers without signing in):

curl -sk https://127.0.0.1/healthz -H 'Host: <DICOOGLE_HOST>'
ok

Step 5: Sign in to the web UI

Open DICOOGLE_URL (for example https://203.0.113.10/) in a browser, accept this instance's certificate after comparing its fingerprint, and sign in as dicoogle with DICOOGLE_ADMIN_PASSWORD.

The Dicoogle sign-in page served over HTTPS by the instance The Search page queries the index (for example PatientName:Doe* or a free-text search), shows the patients, studies, series and images, and previews an image; Management holds the index, transfer, service, storage server and log settings; Indexer indexes files already on the instance.

A search in the Dicoogle web UI finding a synthetic test study (no patient data) by its patient ID

The Dicoogle Management page, Services and Plugins: the called AE title and the storage and query/retrieve services

A wrong password, or the old default dicoogle/dicoogle, gives no session. Sign-in attempts are limited per client address (10 a minute, bursts of 5); beyond that nginx answers 429.

Step 6: Connect a modality or a viewer (DICOM)

The DICOM settings of this instance:

sudo grep -E '^DICOOGLE_(CALLED_AE|CALLING_AE|STORAGE_PORT|QR_PORT)=' /root/dicoogle-credentials.txt
DICOOGLE_CALLED_AE=DICOOGLE-STORAGE
DICOOGLE_CALLING_AE=CLOUDIMG-7F3A9C21
DICOOGLE_STORAGE_PORT=6666
DICOOGLE_QR_PORT=1045

DICOOGLE_CALLING_AE is random and different on every instance. To let a modality, a PACS or a DICOM viewer reach Dicoogle:

  1. Open the security group for that host only: TCP 6666 (storage) and, if it queries or retrieves, TCP 1045, from <modality-ip>/32.
  2. Add it as a DICOM peer on the instance (this also lets Dicoogle send C-MOVE results to it):

    text sudo dicoogle-dicom peer add <modality-ip>/32 sudo dicoogle-dicom peer list

  3. Allow its calling AE title, or configure the modality to use DICOOGLE_CALLING_AE:

    text sudo dicoogle-dicom ae add MODALITY01 sudo dicoogle-dicom ae list

On the modality, set the destination to the instance's address, port 6666 and the called AE title DICOOGLE-STORAGE. A sender whose calling AE title is not on the list is refused for C-STORE and C-MOVE. Each change restarts Dicoogle, which ends every web session. sudo dicoogle-dicom peer remove CIDR and sudo dicoogle-dicom ae remove TITLE undo a change; the last AE title cannot be removed.

Important: Dicoogle 3.6.0 does not apply the calling AE list to C-FIND (queries) or C-ECHO. Anyone who can reach port 1045 can query the index, so the security group and the peer list are what protect your patient metadata: keep both limited to the exact hosts that need DICOM. Peers can be single addresses or networks no wider than a /8 (IPv4) or /32 (IPv6); metadata, link-local, loopback, multicast and documentation ranges are always refused.

Step 7: Send studies to another PACS (C-MOVE)

A C-MOVE destination is a DICOM node Dicoogle sends images to. Add it in the web UI (Management, Storage Servers) with its AE title, address and port. A destination on a public address works as it is. A destination on a private address, or anywhere in this VPC, must be a DICOM peer first:

sudo dicoogle-dicom peer add <pacs-ip>/32

Without that, a move to a private address or to this VPC fails (the connection is refused). The metadata service, link-local and loopback addresses can never be added.

Step 8: The REST API

Sign in with a POST (never put the password in a URL; nginx refuses that with 400) and send the token in the Authorization header. A small example from the instance, reading the password from the root-only file without showing it:

TOKEN="$(sudo awk -F= '/^DICOOGLE_ADMIN_PASSWORD=/{print $2}' /root/dicoogle-credentials.txt \
  | curl -sk https://127.0.0.1/login -H 'Host: <DICOOGLE_HOST>' --data-urlencode 'username=dicoogle' --data-urlencode 'password@-' \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')"
curl -sk 'https://127.0.0.1/search?query=PatientName:*&keyword=true&provider=lucene' -H 'Host: <DICOOGLE_HOST>' -H "Authorization: ${TOKEN}"

Without a token every data and management call answers 401. The full API is described in the Dicoogle learning pack.

Step 9: Check the whole instance

The self-test proves the web UI and its headers over HTTPS, that a wrong password and dicoogle/dicoogle are refused and this instance's password signs in, that the data and management APIs refuse a missing session, that a C-STORE from an unknown AE title is refused, and that a synthetic object (no patient data) is stored, indexed, found, previewed and then deleted again:

sudo dicoogle-selftest
dicoogle-selftest: web UI over 443 with HSTS, CSP (frame-ancestors 'none'), Referrer-Policy same-origin, nosniff; /healthz ok
dicoogle-selftest: wrong password and dicoogle/dicoogle refused (no cookie); query-string password 400; this instance's administrator signed in (cookie Secure, HttpOnly, SameSite=Strict)
dicoogle-selftest: 21 data / management APIs answer 401 without a session; 5 answer 200 with it
dicoogle-selftest: a C-STORE from an AE title outside the allow-list was refused (aborted)
dicoogle-selftest: a SYNTHETIC object (no patient data) from this instance's calling AE was stored, indexed, found and previewed (PNG)
dicoogle-selftest: the synthetic object was unindexed and deleted (the search finds nothing; the file and its empty directories are gone)
dicoogle-selftest: sign-out ended the session
DICOOGLE_SELFTEST_OK

Step 10: The VPC ranges and a fail-closed start

The outbound block refuses Dicoogle every private address and this instance's own VPC ranges. The ranges it read at boot:

sudo dicoogle-vpc-cidrs --print
source imds
4 172.31.0.0/16

If Dicoogle does not start after a reboot. The block is fail closed: the instance reads its VPC ranges from EC2 metadata at every boot (with retries), then from cloud-init's copy, then, on later boots only, from the last list this instance recorded. If none of them gives the ranges (for example after the metadata service was switched off on the instance), Dicoogle refuses to start and journalctl -u dicoogle -u cloudimg-dicoogle-vpc-cidrs says so. Turn IMDSv2 back on (aws ec2 modify-instance-metadata-options --instance-id <id> --http-endpoint enabled --http-tokens required), then:

sudo systemctl restart cloudimg-dicoogle-vpc-cidrs cloudimg-dicoogle-egress dicoogle nginx

Step 11: Storage for your archive

Received images are stored under /var/lib/dicoogle/storage and indexed under /var/lib/dicoogle/index, on the root volume. To make room, either grow the root volume (modify the EBS volume, then sudo growpart /dev/nvme0n1 1 && sudo resize2fs /dev/nvme0n1p1), or attach a dedicated EBS volume and mount it on /var/lib/dicoogle/storage before you receive images: stop Dicoogle (sudo systemctl stop dicoogle), format and mount the volume with an /etc/fstab entry by UUID, then sudo chown dicoogle:dicoogle /var/lib/dicoogle/storage && sudo chmod 0750 /var/lib/dicoogle/storage and sudo systemctl start dicoogle. Back up both directories (EBS snapshots or AWS Backup) and treat the snapshots as patient data.

Step 12: Change the administrator password

sudo dicoogle-set-password            # generates a new random password into /root/dicoogle-credentials.txt
sudo dicoogle-set-password --stdin    # or reads your own (12 characters or more) from standard input

Each restarts Dicoogle and ends every signed-in session.

Step 13: Use your own domain and certificate

Point a DNS name at the instance, then make it the instance's name. The command writes the nginx host names (requests for any other name are dropped), generates a new self-signed certificate for the names and restarts nginx:

sudo dicoogle-set-domain pacs.example.com

To use a certificate from a certificate authority instead, put the full chain and key in /etc/ssl/dicoogle/dicoogle.crt and /etc/ssl/dicoogle/dicoogle.key (root only, mode 0600 for the key), then sudo nginx -t && sudo systemctl reload nginx. If the instance's public address changes after a stop and start, run dicoogle-set-domain again with the new address.

Step 14: Versions, licences and source

cat /usr/share/doc/dicoogle/README.cloudimg

The file names the Dicoogle release, its upstream tag and commit, and the SHA-256 hash of the release zip, of dicoogle.jar and of both plugins.

Dicoogle is free software under the GNU General Public License version 3 or later; the licence texts are in /usr/share/doc/dicoogle. The image ships the upstream binaries unmodified; the corresponding source is the upstream tag 3.6.0 at github.com/bioinformatics-ua/dicoogle.

Ubuntu's unattended upgrades keep the operating system, OpenJDK and nginx patched. Dicoogle does not update automatically: the cloudimg listing adds a new version when the project releases one. Dicoogle 3.6.0 bundles older Java libraries (Jetty 9.0, Jackson 2.9) that have published advisories. On this image they are not reachable without signing in: Jetty listens on the loopback address behind nginx, Dicoogle does not deserialise data from the network, and every API that parses a request body needs the administrator's session. To keep your work across a move to a new instance, copy /var/lib/dicoogle/storage and /var/lib/dicoogle/index, or re-index the copied storage from the Indexer page.

Dicoogle is developed by the Bioinformatics group of the University of Aveiro (IEETA). cloudimg packages and configures it and is not affiliated with or endorsed by the Dicoogle project. Dicoogle on this image is not a certified medical device; check your own regulatory requirements before clinical use.

Support

cloudimg provides support for this image by email at support@cloudimg.co.uk, 24 hours a day, 7 days a week. Tell us the AMI ID and Region, and include the output of sudo dicoogle-selftest and systemctl status dicoogle nginx cloudimg-dicoogle-vpc-cidrs. For questions about Dicoogle itself, see the Dicoogle learning pack and the Dicoogle project on GitHub.