E1
Streaming & Messaging AWS

ElasticMQ 1.7.1 on AWS User Guide

| Product: ElasticMQ 1.7.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the ElasticMQ 1.7.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg streaming and messaging listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

ElasticMQ is a message queue with an Amazon SQS-compatible API. Applications written for Amazon SQS (the AWS CLI, boto3, the AWS SDKs for JavaScript, Java, Go and others, Spring Cloud AWS) point their endpoint at ElasticMQ and keep working, with no AWS account, no per-request charge and no traffic leaving your network. It is 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. What the cloudimg image adds:

  • Every request must be signed. A gate written by cloudimg sits in front of ElasticMQ and verifies the AWS Signature Version 4 of every request against an access key pair that is 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 the 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 made on your instance for its own names and addresses. It can only vouch 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 run in sandboxes that may talk to the loopback address only. The only public ports are 22 (SSH) and 9324 (HTTPS).
  • No browser can drive it. 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. The key pair is handed to the gate by systemd, never placed on a command line, and the gate never logs the signature, the access key id or the query string. 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, 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.

What is on the image: ElasticMQ 1.7.1 in /opt/elasticmq (service elasticmq), OpenJDK 21 (headless JRE), nginx, the cloudimg 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 (the ElasticMQ UI is a separate project and 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 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 (boto3, the AWS SDK for JavaScript v3, and so on).

Step 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 your Region, then Launch through EC2. Choose m5.large (or larger for big in-memory queues), your key pair and a security group as above.

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=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. If you want an address that survives a stop and start, associate an Elastic IP before you configure your clients (Step 11 explains why).

Step 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 summarises where everything is.

Step 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: SSH (22) and the HTTPS SQS endpoint (9324, nginx). Loopback only: ElasticMQ (java, 19324) and the signature gate (python3, 19325).

Step 4: 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 (or its private address when it has none, or the name you set in Step 11).
  • ELASTICMQ_REGION: always elasticmq. Your client must sign with this region; any other region is refused.
  • ELASTICMQ_ACCESS_KEY_ID / 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 (the key pair, 0600) and /root/elasticmq-aws-config (region, endpoint and certificate pin). Keep the credentials file as secret as the instance's SSH key.

Step 5: Check the certificate and pin it in your client

The endpoint's certificate is self-signed, made 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 = cloudimg
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 (CA:FALSE: it can never sign anything else), and 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 that is the profile's ca_bundle setting (or --ca-bundle, verify=), with Node.js an https.Agent whose ca is this file. Do not add it to your operating system's or browser's trust store. If you would rather use a certificate from a CA you trust, see Step 11.

Step 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 (ubuntu can read them with sudo; 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 that carries the endpoint, the region and the pin:

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

Use it like 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.

Step 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.

Step 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, the region to elasticmq, load the key pair from a credentials file or the environment, and trust elasticmq-ca.pem for this client only. (The AWS CLI v2, boto3 and the JavaScript v3 SDK are the clients cloudimg tests this image with.)

Step 9: 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, 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).

Step 10: Persistence (in memory by default)

ElasticMQ keeps queues and messages in memory: they are lost when ElasticMQ restarts or the instance reboots. That is ElasticMQ's default and suits development, test and CI, where queues are created by the application at start-up. ElasticMQ 1.7.1 can also persist queue definitions to a file and messages to an embedded H2 database (the queues-storage and messages-storage sections of /etc/elasticmq/elasticmq.conf, under /var/lib/elasticmq, the only directory the service may write). cloudimg ships it switched off and does not claim durability: in cloudimg'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.

Step 11: Your own name, a CA-issued certificate, a 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 you point at the instance (for example on an Elastic IP):

sudo elasticmq-set-hostname queue.example.com

It re-issues the certificate (same key) to cover the name, updates the endpoint in /root/elasticmq-credentials.txt and /root/elasticmq-aws-config, reloads nginx and restarts ElasticMQ so new queue URLs use the name (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 (then clients 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; sudo elasticmq-set-hostname <name> goes back to a new self-signed one.

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 (OpenSSL-based clients pin the exact certificate). With an Elastic IP or your own name nothing changes.

Step 12: Rotate the access key

sudo elasticmq-rotate-key

This 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, ElasticMQ keeps its queues and messages. Copy the new credentials to your clients.

Step 13: The metadata block and re-verifying 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-cloudimg-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 proves all of it on the running instance: the exact listening set, the loopback-only ports refusing connections on the private address, the metadata service 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

Step 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 (taken from the connection, 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 (queues created at start-up, persistence): /etc/elasticmq/elasticmq.conf. Keep rest-sqs.bind-hostname = "127.0.0.1": 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; raise rate and burst in /etc/nginx/sites-available/cloudimg-elasticmq for heavy load tests.
  • Versions and checksums of everything installed: /usr/share/doc/cloudimg-elasticmq/PROVENANCE.

Security notes

  • 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's port 9324 to the networks your applications connect from, and port 22 to your admin network.
  • Never expose ElasticMQ's own 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 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 cloudimg Azure Marketplace image of ElasticMQ serves the SQS API on port 9324 without authentication (a network security group is its only protection) and 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

cloudimg provides support for this image by email at support@cloudimg.co.uk. 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.