Deployment guide

ElasticMQ 1.7.1

Overview

ElasticMQ is a message queue with an Amazon SQS-compatible API. Applications written for Amazon SQS, such as the AWS CLI, boto3, the AWS SDKs for JavaScript, Java and Go, and Spring Cloud AWS, keep working when you point their endpoint at ElasticMQ. There is no AWS account, no per-request charge and no traffic leaving your network, which makes it a good fit for development, CI and integration testing, demos and air-gapped environments.

ElasticMQ itself has no authentication. It accepts any request and never checks the AWS signature that SQS clients send, so anyone who can reach its port can create, read, purge and delete every queue. This guide covers the SecureAnyCloud ElasticMQ 1.7.1 on Ubuntu 24.04 AMI for Amazon EC2, which closes that gap. It is one version of the SecureAnyCloud streaming and messaging listing on AWS Marketplace, selected as the software version when you subscribe and launch.

What you get:

  • Signed requests only. A gate written by SecureAnyCloud sits in front of ElasticMQ and verifies the AWS Signature Version 4 of every request against an access key pair generated on your instance at first boot. Only a request signed with that key, within five minutes of the instance's clock, reaches ElasticMQ. Anything else gets an SQS error your SDK already understands: InvalidClientTokenId (no signature, an unknown key, a session token) or SignatureDoesNotMatch (a wrong secret, a tampered request, the wrong region, a request outside the time window).
  • HTTPS only. The SQS endpoint is https://<your-instance>:9324, served by nginx with a certificate generated on your instance for its own names and addresses. The certificate can vouch only for this endpoint and can never sign other certificates. You pin it in your client (Step 5). There is no plain-HTTP listener.
  • ElasticMQ on the loopback address. ElasticMQ listens on 127.0.0.1:19324 and the signature gate on 127.0.0.1:19325. Both refuse connections from other machines even if a security group allows them, and both run in sandboxes that may talk to the loopback address only. The only public ports are 22 (SSH) and 9324 (HTTPS).
  • No browser access. Requests that carry an Origin header, a cross-site browser fetch or a WebSocket upgrade are refused before the gate, and cross-origin (CORS) response headers are removed. Every state-changing SQS action also needs a signature, so a link or page on another site cannot delete your queues.
  • No secret on a command line or in a log. systemd hands the key pair to the gate, so it never appears on a command line. The gate never logs the signature, the access key id or the query string, and nginx logs the path only.
  • Fail closed. If the key file is missing, the gate does not start and nothing reaches ElasticMQ. A listener check stops ElasticMQ, the gate or nginx if any of them ever listens beyond its configuration. None of the services can reach the instance metadata service.
  • A pinned supply chain. ElasticMQ is the official elasticmq-server-all-1.7.1.jar release, checked against its published SHA-256 and run by OpenJDK 21 from Ubuntu's main archive. Ubuntu 24.04 LTS is fully patched at build time with unattended security upgrades on, and SSH accepts keys only.

The image contains ElasticMQ 1.7.1 in /opt/elasticmq (service elasticmq), OpenJDK 21 (headless JRE), nginx, the SecureAnyCloud SigV4 gate (service elasticmq-sigv4-gate), the helpers elasticmq-selfcheck, elasticmq-rotate-key, elasticmq-set-hostname and elasticmq-install-cert, cloud-init and the AWS Systems Manager agent. ElasticMQ 1.7.1 has no web console, and the ElasticMQ UI, which is a separate project, is not installed. No compiler toolchain is installed.

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 that you use for management).
  • A security group that allows TCP 22 (SSH) from your admin network and TCP 9324 (the HTTPS SQS endpoint) from the networks your applications connect from. Nothing else needs to be open.
  • On the machine your applications run on: the AWS CLI v2, or an AWS SDK such as boto3 or the AWS SDK for JavaScript v3.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick ElasticMQ 1.7.1 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.

Choose m5.large (or larger for big in-memory queues), your key pair and a security group as described above.

To launch with the AWS CLI instead, 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=elasticmq}]'

The image requires IMDSv2 (session tokens).

First boot takes under a minute after the instance is running. The boot service creates the access key and the certificate, then ElasticMQ, the gate and nginx start.

For an address that survives a stop and start, associate an Elastic IP before you configure your clients (Step 11 explains why).

2

Connect to your instance

Version SSH user

ElasticMQ 1.7.1 on Ubuntu 24.04 ubuntu

ssh -i <your-key.pem> ubuntu@<public-ip>

The login banner summarizes where everything is.

3

Check the services and the listening set

systemctl is-active elasticmq-metadata-names elasticmq-firstboot elasticmq elasticmq-sigv4-gate nginx
sudo ss -tlnp | grep -E ':(22|9324|19324|19325) '
active active active active active LISTEN 0 511 0.0.0.0:9324 0.0.0.0:* users:(("nginx",pid=1241,fd=6),("nginx",pid=1240,fd=6),("nginx",pid=1239,fd=6)) LISTEN 0 128 127.0.0.1:19325 0.0.0.0:* users:(("python3",pid=1183,fd=3)) LISTEN 0 100 127.0.0.1:19324 0.0.0.0:* users:(("java",pid=1019,fd=10)) LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1133,fd=3),("systemd",pid=1,fd=57)) LISTEN 0 511 [::]:9324 [::]:* users:(("nginx",pid=1241,fd=7),("nginx",pid=1240,fd=7),("nginx",pid=1239,fd=7)) LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=1133,fd=4),("systemd",pid=1,fd=58))

Public ports: SSH (22) and the HTTPS SQS endpoint (9324, nginx). Loopback-only ports: ElasticMQ (java, 19324) and the signature gate (python3, 19325).

4

Find your endpoint and access key

First boot wrote /root/elasticmq-credentials.txt (root-only, 0600). List what it holds without printing the secret:

sudo grep -oE '^[A-Z0-9_]+=' /root/elasticmq-credentials.txt
sudo grep -E '^ELASTICMQ_(HOST|ENDPOINT|REGION|TLS_CERT_SHA256)=' /root/elasticmq-credentials.txt
ELASTICMQ_HOST= ELASTICMQ_ENDPOINT= ELASTICMQ_REGION= ELASTICMQ_ACCOUNT_ID= ELASTICMQ_ACCESS_KEY_ID= ELASTICMQ_SECRET_ACCESS_KEY= ELASTICMQ_TLS_CERT_FILE= ELASTICMQ_TLS_CERT_SHA256= ELASTICMQ_TLS_PUBKEY_SHA256= ELASTICMQ_PROFILE= ELASTICMQ_HOST=<elasticmq-host> ELASTICMQ_ENDPOINT=https://<elasticmq-host>:9324 ELASTICMQ_REGION=elasticmq ELASTICMQ_TLS_CERT_SHA256=20:6A:D3:35:FC:12:68:9B:97:38:EC:56:8A:D1:E7:97:A1:DE:AD:11:9B:41:FF:C1:83:65:17:9C:FF:6D:C8:40
  • ELASTICMQ_ENDPOINT: the SQS endpoint URL. The host is this instance's public IPv4 address, its private address when it has no public one, or the name you set in Step 11.
  • ELASTICMQ_REGION: always elasticmq. Your client must sign with this region, and any other region is refused.
  • ELASTICMQ_ACCESS_KEY_ID and ELASTICMQ_SECRET_ACCESS_KEY: the key pair your clients sign with. They exist only on this instance.

The same key pair is ready to copy as an AWS profile named elasticmq. /root/elasticmq-aws-credentials holds the key pair (0600), and /root/elasticmq-aws-config holds the region, endpoint and certificate pin. Keep the credentials file as secret as the instance's SSH key.

5

Check the certificate and pin it in your client

The endpoint's certificate is self-signed and generated on your instance:

openssl x509 -in /etc/elasticmq/tls/elasticmq.crt -noout -subject -ext subjectAltName,basicConstraints -fingerprint -sha256
openssl s_client -connect 127.0.0.1:9324 -servername localhost </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256
subject=CN = elasticmq, O = sac X509v3 Subject Alternative Name: IP Address:<elasticmq-host>, DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<private-ip>, DNS:ip-<private-ip-dashed>.ec2.internal, IP Address:127.0.0.1, DNS:localhost X509v3 Basic Constraints: critical CA:FALSE sha256 Fingerprint=20:6A:D3:35:FC:12:68:9B:97:38:EC:56:8A:D1:E7:97:A1:DE:AD:11:9B:41:FF:C1:83:65:17:9C:FF:6D:C8:40 sha256 Fingerprint=20:6A:D3:35:FC:12:68:9B:97:38:EC:56:8A:D1:E7:97:A1:DE:AD:11:9B:41:FF:C1:83:65:17:9C:FF:6D:C8:40

The certificate names this instance's public and private addresses and DNS names. It is CA:FALSE, so it can never sign anything else. Port 9324 serves exactly the file whose fingerprint is recorded in the credentials file.

Copy the certificate to the machine your application runs on and compare its fingerprint with ELASTICMQ_TLS_CERT_SHA256. Then pin it for this endpoint only. With the AWS CLI and boto3, use the profile's ca_bundle setting (or --ca-bundle, verify=). With Node.js, use an https.Agent whose ca is this file.

Do not add the certificate to your operating system's or browser's trust store. To use a certificate from a CA you trust instead, see Step 11.

6

Set up the AWS CLI on your workstation

On the machine your applications run on, copy the profile, the key pair and the certificate from the instance. The ubuntu user can read them with sudo, and nothing is printed on your screen:

# workstation$ (your own machine; replace the angle brackets)
mkdir -p ~/.aws && chmod 700 ~/.aws ssh -i <your-key.pem> ubuntu@<elasticmq-host> 'cat /etc/elasticmq/tls/elasticmq.crt' > ~/.aws/elasticmq-ca.pem openssl x509 -in ~/.aws/elasticmq-ca.pem -noout -fingerprint -sha256
# must equal ELASTICMQ_TLS_CERT_SHA256 ssh -i <your-key.pem> ubuntu@<elasticmq-host> 'sudo cat /root/elasticmq-aws-credentials' >> ~/.aws/credentials ssh -i <your-key.pem> ubuntu@<elasticmq-host> 'sudo cat /root/elasticmq-aws-config' >> ~/.aws/config chmod 600 ~/.aws/credentials

~/.aws/config now has a profile with the endpoint, the region and the pin:

[profile elasticmq] region = elasticmq endpoint_url = https://<elasticmq-host>:9324 ca_bundle = ~/.aws/elasticmq-ca.pem

Use the profile as you would use Amazon SQS:

# workstation$ aws --profile elasticmq sqs create-queue --queue-name orders aws --profile elasticmq sqs send-message --queue-url https://<elasticmq-host>:9324/000000000000/orders --message-body '{"order": 1001}' aws --profile elasticmq sqs receive-message --queue-url https://<elasticmq-host>:9324/000000000000/orders --wait-time-seconds 5 aws --profile elasticmq sqs delete-message --queue-url https://<elasticmq-host>:9324/000000000000/orders --receipt-handle '<receipt-handle>' aws --profile elasticmq sqs list-queues
{ "QueueUrl": "https://<elasticmq-host>:9324/000000000000/orders" } { "MD5OfMessageBody": "de7c563ab7e55546a0e42f887e54dae1", "MessageId": "b4574f15-d799-4436-955e-4cec3c6cf898" } { "Messages": [ { "MessageId": "b4574f15-d799-4436-955e-4cec3c6cf898", "ReceiptHandle": "b4574f15-d799-4436-955e-4cec3c6cf898#7330331e-dbb1-45d9-bb27-36197093bb8f", "MD5OfBody": "de7c563ab7e55546a0e42f887e54dae1", "Body": "{\"order\": 1001}" } ] } { "QueueUrls": [ "https://<elasticmq-host>:9324/000000000000/orders" ] }

Queue URLs always have the form https://<elasticmq-host>:9324/000000000000/<queue-name> (000000000000 is ElasticMQ's account id).

FIFO queues (.fifo names with FifoQueue=true), dead-letter queues, delays and visibility timeouts work as in the SQS API that ElasticMQ implements.

7

Python (boto3)

boto3 reads the same profile, so the endpoint, the region and the certificate pin need no code:

import boto3
# endpoint_url, region and the pinned certificate (ca_bundle) come from the "elasticmq" profile sqs = boto3.Session(profile_name="elasticmq").client("sqs") queue_url = sqs.create_queue(QueueName="orders")["QueueUrl"] sqs.send_message(QueueUrl=queue_url, MessageBody='{"order": 1002}') for msg in sqs.receive_message(QueueUrl=queue_url, WaitTimeSeconds=5).get("Messages", []): print("received:", msg["Body"]) sqs.delete_message(QueueUrl=queue_url, ReceiptHandle=msg["ReceiptHandle"]) print("queues:", sqs.list_queues().get("QueueUrls", []))
received: {"order": 1002} queues: ['https://<elasticmq-host>:9324/000000000000/orders']

Without a profile, pass the same three things explicitly: boto3.client("sqs", region_name="elasticmq", endpoint_url="https://<elasticmq-host>:9324", verify="/path/to/elasticmq-ca.pem"), with the key pair from the environment or a credentials file, never in your source code.

8

JavaScript (AWS SDK for JavaScript v3)

import { readFileSync } from "node:fs"; import { Agent } from "node:https"; import { SQSClient, GetQueueUrlCommand, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand } from "@aws-sdk/client-sqs"; import { fromIni } from "@aws-sdk/credential-providers"; import { NodeHttpHandler } from "@smithy/node-http-handler"; const sqs = new SQSClient({ region: "elasticmq", endpoint: process.env.ELASTICMQ_ENDPOINT, // https://<elasticmq-host>:9324 credentials: fromIni({ profile: "elasticmq" }), // the key pair from ~/.aws/credentials requestHandler: new NodeHttpHandler({ // trust this endpoint's certificate only httpsAgent: new Agent({ ca: readFileSync(process.env.HOME + "/.aws/elasticmq-ca.pem") }), }), }); const { QueueUrl } = await sqs.send(new GetQueueUrlCommand({ QueueName: "orders" })); await sqs.send(new SendMessageCommand({ QueueUrl, MessageBody: JSON.stringify({ order: 1003 }) })); const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({ QueueUrl, WaitTimeSeconds: 5 })); for (const m of Messages) { console.log("received:", m.Body); await sqs.send(new DeleteMessageCommand({ QueueUrl, ReceiptHandle: m.ReceiptHandle })); }
# workstation$ (npm install @aws-sdk/client-sqs @aws-sdk/credential-providers @smithy/node-http-handler) ELASTICMQ_ENDPOINT=https://<elasticmq-host>:9324 node sqs-demo.mjs
received: {"order":1003}

Other AWS SDKs are configured the same way. Set the endpoint override to https://<elasticmq-host>:9324 and the region to elasticmq. Load the key pair from a credentials file or the environment, and trust elasticmq-ca.pem for this client only.

SecureAnyCloud tests this image with the AWS CLI v2, boto3 and the JavaScript v3 SDK.

9

See what is refused, and why

ElasticMQ does not check who is calling, so the gate does. A request without a signature is refused even from the instance itself:

curl -s --cacert /etc/elasticmq/tls/elasticmq.crt "https://127.0.0.1:9324/?Action=ListQueues&Version=2012-11-05"
<?xml version="1.0"?> <ErrorResponse xmlns="http://queue.amazonaws.com/doc/2012-11-05/"><Error><Type>Sender</Type><Code>InvalidClientTokenId</Code><Message>Request is missing Authentication Token: this endpoint requires AWS Signature Version 4 with this instance's access key.</Message><Detail/></Error><RequestId>772d9a1c-5d1d-400d-822a-1b14bfdde480</RequestId></ErrorResponse>

From your workstation, try an unsigned request, the wrong region and a client that does not pin the certificate:

# workstation$ aws sqs list-queues --region elasticmq --endpoint-url https://<elasticmq-host>:9324 --ca-bundle ~/.aws/elasticmq-ca.pem --no-sign-request aws --profile elasticmq sqs list-queues --region us-east-1 aws sqs list-queues --region elasticmq --endpoint-url https://<elasticmq-host>:9324
aws: [ERROR]: An error occurred (InvalidClientTokenId) when calling the ListQueues operation: Request is missing Authentication Token: this endpoint requires AWS Signature Version 4 with this instance's access key. aws: [ERROR]: An error occurred (SignatureDoesNotMatch) when calling the ListQueues operation: Credential should be scoped to a valid region, not 'us-east-1'. Use region 'elasticmq' for this endpoint. aws: [ERROR]: SSL validation failed for https://<elasticmq-host>:9324/ [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1082)

Request Answer

No signature (including any link or page on another site, such as GET ?Action=DeleteQueue) 403 InvalidClientTokenId

An access key id this instance did not issue, or a session token (X-Amz-Security-Token) 403 InvalidClientTokenId

The right key id with the wrong secret, a changed body, header or host 403 SignatureDoesNotMatch

Signed for a region other than elasticmq, or a service other than sqs 403 SignatureDoesNotMatch

A timestamp more than 5 minutes from the instance's clock 403 SignatureDoesNotMatch (Signature expired)

An Origin header, Sec-Fetch-Site: cross-site or same-site, a WebSocket upgrade 403 (nginx)

A method other than GET or POST 405

A body over 2 MiB 413

A Host that is not one of this instance's names connection closed, no answer

Presigned query-string requests (X-Amz-Signature in the URL) are accepted for requests without a body, for at most one hour. Keep your clients' clocks in sync. Amazon EC2 instances use the Amazon Time Sync Service by default.

10

Understand persistence (in memory by default)

ElasticMQ keeps queues and messages in memory. They are lost when ElasticMQ restarts or the instance reboots. This is ElasticMQ's default, and it suits development, test and CI, where the application creates queues at start-up.

ElasticMQ 1.7.1 can also persist queue definitions to a file and messages to an embedded H2 database. Configure this in the queues-storage and messages-storage sections of /etc/elasticmq/elasticmq.conf. The data lives under /var/lib/elasticmq, the only directory the service may write.

SecureAnyCloud ships persistence switched off and does not claim durability. In SecureAnyCloud's testing of 1.7.1, queue definitions came back after a restart, but messages did not reliably survive an ungraceful stop. Use Amazon SQS or another broker in this listing for data you cannot afford to lose.

The JVM may use up to half of the instance's memory (-XX:MaxRAMPercentage=50 in elasticmq.service). Pick an instance size for the backlog you expect.

11

Use your own name, a CA-issued certificate, and stop and start

The endpoint host, the certificate's names, nginx's accepted names and the queue URLs all come from configuration, never from a request. To use a DNS name that you point at the instance, for example on an Elastic IP, run:

sudo elasticmq-set-hostname queue.example.com

The command re-issues the certificate (same key) to cover the name and updates the endpoint in /root/elasticmq-credentials.txt and /root/elasticmq-aws-config. It reloads nginx and restarts ElasticMQ so new queue URLs use the name, which means in-memory queues are lost. Copy the new certificate to your clients afterwards.

To serve a certificate from your own CA or a public CA instead, run the command below. Clients then trust that CA and need no pin:

sudo elasticmq-install-cert /path/to/fullchain.pem /path/to/privkey.pem queue.example.com

The chain file holds the server certificate first, then any intermediates. Boot-time renewal never replaces an installed certificate. Run sudo elasticmq-set-hostname <name> to go back to a new self-signed certificate.

Stop and start: without an Elastic IP, an EC2 stop and start gives the instance a new public IP. At the next boot, the certificate is re-issued for the new addresses and the endpoint in the credentials and profile files is updated. Copy the certificate and the endpoint to your clients again, because OpenSSL-based clients pin the exact certificate. With an Elastic IP or your own name, nothing changes.

12

Rotate the access key

sudo elasticmq-rotate-key

This command mints a new key pair, writes it to /root/elasticmq-credentials.txt and /root/elasticmq-aws-credentials, and restarts only the signature gate. The old key is refused immediately, and ElasticMQ keeps its queues and messages. Copy the new credentials to your clients.

13

Verify the metadata block and re-check the appliance

ElasticMQ, the gate, nginx and the boot service are denied the instance metadata service (169.254.169.254 and its IPv6 and IPv4-mapped forms). ElasticMQ and the gate may reach the loopback address only:

grep -h '^IPAddress' /etc/systemd/system/elasticmq.service /etc/systemd/system/elasticmq-sigv4-gate.service /etc/systemd/system/nginx.service.d/10-sac-elasticmq.conf | sort | uniq -c
2 IPAddressAllow=localhost 3 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128 2 IPAddressDeny=any

elasticmq-selfcheck verifies all of this on the running instance. It checks the exact listening set, the loopback-only ports refusing connections on the private address, the metadata service being unreachable from inside each service, the certificate, a signed round trip in both SQS protocols, and every refusal in the table above:

sudo elasticmq-selfcheck
ok elasticmq.service active ok elasticmq-sigv4-gate.service active ok nginx.service active ok elasticmq.service listens exactly on 127.0.0.1:19324 and holds no udp socket ok elasticmq-sigv4-gate.service listens exactly on 127.0.0.1:19325 and holds no udp socket ok nginx.service listens exactly on 0.0.0.0:9324 [::]:9324 and holds no udp socket ok host public TCP 0.0.0.0:22 0.0.0.0:9324 [::]:22 [::]:9324; loopback 127.0.0.1:19324 127.0.0.1:19325 127.0.0.53%lo:53 127.0.0.54:53 ok port 19324 refuses connections on the private address <private-ip> (curl exit 7) ok port 19325 refuses connections on the private address <private-ip> (curl exit 7) ok metadata unreachable from inside elasticmq.service (169.254.169.254 v4 + v4-mapped, 168.63.129.16: 000); loopback control 200 ok metadata unreachable from inside elasticmq-sigv4-gate.service (169.254.169.254 v4 + v4-mapped, 168.63.129.16: 000); loopback control 403 ok metadata unreachable from inside nginx.service (169.254.169.254 v4 + v4-mapped, 168.63.129.16: 000); loopback control 403 ok certificate /etc/elasticmq/tls/elasticmq.crt: end-entity server certificate naming <elasticmq-host> ok 9324 serves /etc/elasticmq/tls/elasticmq.crt (SHA-256 20:6A:D3:35:FC:12:68:9B...), as recorded in the credentials file ok signed JSON-protocol round trip: CreateQueue, SendMessage, ReceiveMessage (body matches), DeleteMessage 200 ok signed query-protocol ListQueues lists the queue (200) ok unsigned GET ?Action=DeleteQueue (a cross-site style request) refused: 403 InvalidClientTokenId ok tampered signature refused: 403 SignatureDoesNotMatch ok unknown access key refused: 403 InvalidClientTokenId ok wrong secret refused: 403 SignatureDoesNotMatch ok wrong region refused: 403 SignatureDoesNotMatch ok request 600 s old refused: 403 SignatureDoesNotMatch ok session token refused: 403 InvalidClientTokenId ok signed request with a foreign Origin refused by nginx: 403 ok signed request with Sec-Fetch-Site: cross-site refused: 403 ok WebSocket upgrade refused: 403 ok PUT refused: 405 ok 3 MiB body refused: 413 ok unknown Host closed with no answer: connection closed ok TLS without the pin refused (the self-signed certificate is not trusted) ok the queue still exists after the refused requests (GetQueueUrl 200) ok signed DeleteQueue removed it (200, then GetQueueUrl com.amazonaws.sqs#QueueDoesNotExist) SQS-CHECK OK SELFCHECK OK
14

Logs and service management

sudo journalctl -u elasticmq-sigv4-gate --no-pager -o cat | tail -4
elasticmq-sigv4-gate: refused SignatureDoesNotMatch reason=signature-mismatch from=127.0.0.1 method=POST elasticmq-sigv4-gate: refused SignatureDoesNotMatch reason=wrong-region from=127.0.0.1 method=POST elasticmq-sigv4-gate: refused SignatureDoesNotMatch reason=time-window from=127.0.0.1 method=POST elasticmq-sigv4-gate: refused InvalidClientTokenId reason=session-token from=127.0.0.1 method=POST

Each refusal is logged with its reason and the client's real address, which is taken from the connection and never from a header. ElasticMQ logs queue creation and deletion to journalctl -u elasticmq. nginx writes /var/log/nginx/elasticmq-access.log with the method and path only.

  • Restart: sudo systemctl restart elasticmq (in-memory queues are lost), sudo systemctl restart elasticmq-sigv4-gate, sudo systemctl reload nginx.
  • ElasticMQ's own settings, including queues created at start-up and persistence, live in /etc/elasticmq/elasticmq.conf. Keep rest-sqs.bind-hostname = "127.0.0.1", because the listener check stops ElasticMQ if it listens anywhere else.
  • nginx allows 200 requests per second per client address (bursts of 1000) and 256 connections per address. For heavy load tests, raise rate and burst in /etc/nginx/sites-available/sac-elasticmq.
  • Versions and checksums of everything installed are in /usr/share/doc/sac-elasticmq/PROVENANCE.

Security notes

  • Protect the key pair. Keep /root/elasticmq-credentials.txt and /root/elasticmq-aws-credentials secret, and give the key pair only to the applications that need the queues. Rotate it with elasticmq-rotate-key when someone should lose access.
  • Restrict the security group. Limit port 9324 to the networks your applications connect from, and port 22 to your admin network.
  • Keep ElasticMQ's own port private. Never expose port 19324 or bind it to a public address. ElasticMQ has no authentication, and anyone who reached it directly could read, purge and delete every queue.
  • Pin the certificate. Pin the instance's certificate in each client, or install a CA-issued certificate. Never add the self-signed certificate to a system-wide trust store.

What changed since the Azure image

The SecureAnyCloud Azure Marketplace image of ElasticMQ serves the SQS API on port 9324 without authentication, so a network security group is its only protection. It also serves a Basic-auth copy of the API over plain HTTP on port 80.

This AWS image serves the API over HTTPS only and verifies an AWS Signature Version 4 with a per-instance access key on every request, so the AWS CLI and SDKs authenticate the way they do with Amazon SQS. It keeps ElasticMQ on the loopback address, serves nothing on 80 or 443, refuses browser-driven requests and blocks the instance metadata service.

Support

SecureAnyCloud provides support for this image by email at sales@secureanycloud.com.

ElasticMQ is open-source software by SoftwareMill under the Apache License 2.0. This image is not affiliated with or endorsed by SoftwareMill or the ElasticMQ project. Amazon SQS and AWS are trademarks of Amazon.com, Inc. or its affiliates.

Deploy on AWS

Launch ElasticMQ 1.7.1 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS