deepstream.io 10.2.4
Overview
This image runs deepstream.io 10.2.4 on Ubuntu 24.04 on Amazon EC2. It is one version of the SecureAnyCloud streaming and messaging listing on AWS Marketplace, which you pick as the software version when you subscribe and launch. This guide covers launching and using it.
deepstream.io is an open source realtime server. Clients connect over a WebSocket and share records (JSON documents that stay in sync on every client that subscribes to them), events (publish and subscribe), RPCs (one client calls a procedure another client provides) and presence (which users are online). An HTTP API offers the same operations to servers and scripts that do not keep a connection open.
What you get:
- deepstream.io 10.2.4, the release that closes five upstream security advisories. 10.1.x and earlier could be crashed by one unauthenticated WebSocket frame, frozen with a crafted listen pattern, and let clients bypass permission rules through bulk record reads, record updates and listen responses. 10.2.4 fixes all of them.
- TLS only. deepstream listens on
127.0.0.1:6020and nothing else. nginx serves the WebSocket endpoint atwss://<host>/deepstreamand the HTTP API athttps://<host>/apion port 443. Port 80 only redirects. - Authentication that is on, with no default account. Upstream's sample configuration accepts anyone (
auth: none) and its sample user file holds the published loginuserA/password. This image uses file authentication only, ships an empty user list, and generates anadminlogin unique to your instance at first boot. Passwords are stored only as PBKDF2-HMAC-SHA512 digests (210,000 iterations, a random salt per password), and the password itself never appears on a command line. - Least privilege for every user you add. The
adminrole has full access. Any other role can readpublic/*records, read and write its ownusers/<name>/*records and subscribe topublic/*events, and nothing else, until you widen the rules. - A WebSocket Origin guard. deepstream itself accepts a WebSocket upgrade from any web page. nginx refuses an upgrade (and an HTTP API call) whose
Originis another site before it reaches deepstream, so a page on another site cannot use a visitor's browser to talk to your server. Clients that send noOrigin(Node.js, Java, mobile apps, scripts) are not affected. You allow your own web application's origin with one command. - Exact routes, rate limits, sandboxing. deepstream matches its HTTP paths by prefix, but nginx serves only the two exact paths and answers 404 for everything else, including the health check, which stays on the instance. WebSocket connections and API calls are rate limited per client address. deepstream runs as an unprivileged user with systemd sandboxing and cannot reach the instance metadata service.
- HTTPS from the first boot. The certificate is generated on your instance for its own names and addresses. It is an end-entity certificate, so it can only vouch for this instance and can never sign other certificates. Install a CA-issued certificate with one command (Step 12).
- A pinned software supply chain. deepstream and its 136 npm dependencies are installed from a committed lockfile with every package's integrity hash checked and no install scripts run. Node.js is the official release, checked against its signed checksums. 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: deepstream.io 10.2.4 (@deepstream/server) and the official JavaScript client @deepstream/client 7.1.1 in /opt/deepstream, Node.js 26.13.0 LTS in /opt/node (also on the PATH as node and npm), nginx 1.24 (from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent. No compiler toolchain is installed. deepstream keeps records in memory (no storage plugin is configured), so records live as long as the server runs.
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 443 (WSS and HTTPS) from your clients, plus TCP 80 if you want plain HTTP requests redirected to HTTPS. There is nothing else to open. deepstream's own port (6020) listens on the loopback address only and refuses connections from other machines even if a security group allows them.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick deepstream.io 10.2.4 on Ubuntu 24.04 as the software version and select your Region, then choose Launch through EC2. Choose m5.large (or larger for many concurrent connections), your key pair and a security group as described in Prerequisites.
Alternatively, launch with the AWS CLI, replacing 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=deepstream}]'First boot takes about a minute after the instance is running. The boot service generates the admin login and the certificate, then deepstream and nginx start.
Connect to your instance
Connect over SSH as the ubuntu user with your key pair:
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services and the listening set
Confirm that all four services are running and that the first boot completed:
systemctl is-active deepstream-metadata-names deepstream-firstboot deepstream nginx
sudo test -f /var/lib/sac/data/deepstream-firstboot.done && echo FIRSTBOOT_DONEExpected output. All four units are active and the first boot marker is present:
active active active active FIRSTBOOT_DONEOnly SSH and nginx listen on network addresses. deepstream listens on 127.0.0.1:6020:
sudo ss -Hlnt | awk '{print $4}' | grep -v '^127.0.0.5' | sort0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 127.0.0.1:6020 [::]:22 [::]:443 [::]:80Get the URLs, the admin login and the certificate
The credentials file is readable by root only. It holds the WebSocket and HTTP API URLs, the admin user and password, and the SHA-256 fingerprint of this instance's certificate. Print it:
sudo cat /root/deepstream-credentials.txt# deepstream.io 10.2.4 on Ubuntu 24.04 - generated on first boot by deepstream-firstboot.service.
# The admin login is unique to this instance; none was baked into the image. Keep this file secret: the admin role
# has full access to every record, event, RPC and presence. deepstream stores the password only as a PBKDF2-SHA512
# digest (/etc/deepstream/users.yml). Clients connect over TLS only:
# WebSocket wss://<public-ip>/deepstream HTTP API https://<public-ip>/api (POST, JSON)
# The certificate is self signed (minted on this instance): compare its SHA-256 fingerprint with
# DEEPSTREAM_TLS_CERT_SHA256 below and pin it in your client (Node: socketOptions.ca), or install a CA-issued
# certificate with
sudo deepstream-install-cert <chain.pem> <key.pem>
# Add users (password on stdin, stored hashed):
sudo deepstream-user add <name> <role>
# Allow a browser app's origin:
sudo deepstream-allow-origin add https://app.example.com
# Change the served name:
sudo deepstream-set-hostname <dns-name> Re-verify:
sudo deepstream-selfcheck DEEPSTREAM_URL=wss://<public-ip>/deepstream DEEPSTREAM_HTTP_URL=https://<public-ip>/api DEEPSTREAM_HOST=<public-ip> DEEPSTREAM_ADMIN_USER=admin DEEPSTREAM_ADMIN_PASSWORD=<DEEPSTREAM_ADMIN_PASSWORD> DEEPSTREAM_TLS_CERT_SHA256=<sha256-fingerprint> DEEPSTREAM_TLS_CERT_FILE=/etc/sac/deepstream-tls.pem
# User guide: https://secureanycloud.com/stacks/deepstream-io-10-2-4/The URLs, the host and the user (without the password):
sudo grep -E '^DEEPSTREAM_(URL|HTTP_URL|HOST|ADMIN_USER|TLS_CERT_FILE)=' /root/deepstream-credentials.txtDEEPSTREAM_URL=wss://<public-ip>/deepstream DEEPSTREAM_HTTP_URL=https://<public-ip>/api DEEPSTREAM_HOST=<public-ip> DEEPSTREAM_ADMIN_USER=admin DEEPSTREAM_TLS_CERT_FILE=/etc/sac/deepstream-tls.pemTo print just the password on the instance:
sudo awk -F= '$1=="DEEPSTREAM_ADMIN_PASSWORD" {print $2}' /root/deepstream-credentials.txtThe host is the instance's public IP address from the EC2 instance metadata, or the private address when the instance has no public one. If you stop and start the instance without an Elastic IP, the boot service picks up the new address and issues a new certificate for it, unless you installed your own (Step 12).
Check the certificate and pin it in your client
The certificate is self-signed and unique to this instance, so no client trusts it by default. A client that has not been told about it refuses the connection. Do not add it to an operating-system or browser trust store. Instead, compare its fingerprint, then pin it in the one client that connects to this server, for that connection only.
Read the certificate's fingerprint, names and constraints on the instance:
openssl x509 -in /etc/sac/deepstream-tls.pem -noout -fingerprint -sha256
openssl x509 -in /etc/sac/deepstream-tls.pem -noout -ext subjectAltName,basicConstraints,keyUsage,extendedKeyUsagesha256 Fingerprint=<sha256-fingerprint> X509v3 Subject Alternative Name: IP Address:<public-ip>, 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 X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server AuthenticationCopy the certificate file to the machine your client runs on and check that its fingerprint matches DEEPSTREAM_TLS_CERT_SHA256. The file is public, not a secret.
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/sac/deepstream-tls.pem ./deepstream-tls.pem openssl x509 -in deepstream-tls.pem -noout -fingerprint -sha256Then give that file to your client as the only certificate it trusts for this server. In Node.js, use the client's socketOptions: { ca } (Step 6). In other runtimes, use the equivalent per-connection trust setting. Browsers cannot pin a certificate for one site, so for browser clients, install a CA-issued certificate for a DNS name you own (Step 12).
Connect a Node.js client
Clients log in with a user name and password. The password should come from a file or the environment, never from the command line. This example uses the official client, logs in over wss:// with the instance certificate pinned for this connection, and then uses a record, an event, an RPC and presence. On the instance, it connects to wss://localhost/deepstream (the certificate also names localhost), using the copy of the client library in /opt/deepstream:
install -d -m 0700 ~/ds-demo ( umask 077;
sudo awk -F= '$1=="DEEPSTREAM_ADMIN_PASSWORD" {print $2}' /root/deepstream-credentials.txt > ~/ds-demo/admin.pw ) cat > ~/ds-demo/app.js <<'EOF' // deepstream.io client demo: log in over wss:// with this instance's certificate pinned for this connection only, // then use records, events, RPC and presence. The password is read from a file, never the command line. const fs = require('fs') const { DeepstreamClient } = require('@deepstream/client') const url = process.env.DEEPSTREAM_URL || 'wss://localhost/deepstream' const ca = fs.readFileSync(process.env.DEEPSTREAM_CA_FILE || '/etc/sac/deepstream-tls.pem') const password = fs.readFileSync(process.env.DEEPSTREAM_PASSWORD_FILE, 'utf8').trim() const client = new DeepstreamClient(url, { socketOptions: { ca } }) client.on('error', (err, event) => console.error('deepstream error:', event, err)) client.login({ username: process.env.DEEPSTREAM_USER || 'admin', password }, async (ok, data) => { if (!ok) { console.error('login refused:', JSON.stringify(data)); process.exit(1) } console.log('logged in as', process.env.DEEPSTREAM_USER || 'admin', JSON.stringify(data)) // Records: synchronised JSON documents const record = client.record.getRecord('demo/settings') await record.whenReady() await record.setWithAck({ theme: 'dark', updated: new Date().toISOString().slice(0, 10) }) console.log('record demo/settings =', JSON.stringify(record.get())) // Events: publish / subscribe const got = new Promise((resolve) => client.event.subscribe('demo/news', resolve)) client.event.emit('demo/news', { headline: 'hello from deepstream' }) console.log('event demo/news received', JSON.stringify(await got)) // RPC: request / response client.rpc.provide('demo/add', (data, response) => response.send(data.a + data.b)) await new Promise((resolve) => setTimeout(resolve, 500)) // let the provider registration reach the server console.log('rpc demo/add(2, 3) =', await client.rpc.make('demo/add', { a: 2, b: 3 })) // Presence: who is online client.presence.getAll(['admin'], (err, users) => { console.log('presence', JSON.stringify(users)) client.rpc.unprovide('demo/add'); record.discard(); client.close() }) }) EOF cd ~/ds-demo && DEEPSTREAM_PASSWORD_FILE=~/ds-demo/admin.pw NODE_PATH=/opt/deepstream/node_modules node app.jslogged in as admin {"role":"admin"} record demo/settings = {"theme":"dark","updated":"2026-10-29"} event demo/news received {"headline":"hello from deepstream"} rpc demo/add(2, 3) = 5 presence {"admin":true}To run it from your own machine, install the same client (npm install @deepstream/client@7.1.1) and save the password in a file only you can read (chmod 600). Then run the same program against the public URL with the certificate you copied in Step 5:
DEEPSTREAM_URL=wss://<public-ip>/deepstream DEEPSTREAM_CA_FILE=./deepstream-tls.pem DEEPSTREAM_PASSWORD_FILE=./admin.pw node app.jsA wrong password is refused with INVALID_AUTHENTICATION_DETAILS, and a missing user name or password with missing authentication parameter. Each connection may try to log in at most three times, and is closed after 20 seconds without a login.
Records, events, RPC and presence
- Records (
client.record.getRecord(name)) are JSON documents. Every client subscribed to a record receives each change.setWithAckresolves once the server accepted the write, andclient.record.snapshot(name)reads the current value once. Records are kept in deepstream's memory, so configure one of deepstream's storage connectors (in/etc/deepstream/config.yml) if they must survive a restart. - Events (
client.event.subscribe/emit) are fire-and-forget messages to every subscriber of a name. - RPCs (
client.rpc.provide/make) route a request to one client that provides the procedure and return its answer. - Presence (
client.presence.getAll,subscribe) reports which user names are connected.
Names are free-form strings. The permission rules match the / separated prefixes (Step 9).
The HTTP API
The HTTP API runs the same operations without a WebSocket: POST a JSON body with authData and a list of operations to https://<host>/api. Write the body to a file so the password never appears on a command line, and pin the certificate with --cacert:
cd ~/ds-demo ( umask 077 printf '{"authData":{"username":"admin","password":"%s"},"body":[{"topic":"record","action":"write","recordName":"demo/http","data":{"source":"http api"}}]}' "$(cat ~/ds-demo/admin.pw)" > write.json printf '{"authData":{"username":"admin","password":"%s"},"body":[{"topic":"record","action":"read","recordName":"demo/http"},{"topic":"event","action":"emit","eventName":"demo/news","data":{"headline":"via http"}}]}' "$(cat ~/ds-demo/admin.pw)" > read.json ) curl -s --cacert /etc/sac/deepstream-tls.pem -H 'Content-Type: application/json' --data-binary @write.json https://localhost/api; echo curl -s --cacert /etc/sac/deepstream-tls.pem -H 'Content-Type: application/json' --data-binary @read.json https://localhost/api; echo{"result":"SUCCESS","body":[{"success":true}]} {"result":"SUCCESS","body":[{"version":1,"data":{"source":"http api"},"success":true},{"success":true}]}Send each write and the read that depends on it as separate requests, because operations in one request run in parallel. Without valid authData, the API answers 401:
printf '{"body":[{"topic":"record","action":"read","recordName":"demo/http"}]}' > ~/ds-demo/anon.json curl -s -w ' (HTTP %{http_code})\n' --cacert /etc/sac/deepstream-tls.pem -H 'Content-Type: application/json' --data-binary @"$HOME/ds-demo/anon.json" https://localhost/apiUnsuccessful authentication attempt. (HTTP 401)The API accepts only POST with a Content-Type: application/json body (other methods 405, other body types 415), and a browser request from another site is refused 403 (Step 10). From another machine, use DEEPSTREAM_HTTP_URL from the credentials file with --cacert ./deepstream-tls.pem.
Add users and scope their permissions
Users live in /etc/deepstream/users.yml, each with a PBKDF2 digest of its password and a role. Manage them with deepstream-user, which reads the password on standard input (never the command line), stores only the digest, and lists names and roles only. deepstream reads the user list at start, so restart it afterwards:
( umask 077; openssl rand -hex 16 > alice.pw )
sudo deepstream-user add alice app < alice.pw
sudo systemctl restart deepstream
sudo deepstream-user listdeepstream-user: added alice (role app); restart deepstream to apply admin role=admin alice role=appsudo deepstream-user remove <name> removes a user, and refuses to remove the last admin. The role decides what a user may do. /etc/deepstream/permissions.yml gives the admin role full access and every other role least privilege:
sudo sed -n '/^record:/,/^event:/p' /etc/deepstream/permissions.ymlrecord: "*": create: "user.data.role === 'admin'" write: "user.data.role === 'admin'" read: "user.data.role === 'admin'" delete: "user.data.role === 'admin'" listen: "user.data.role === 'admin'" notify: "user.data.role === 'admin'" "public/*": create: "user.data.role === 'admin'" write: "user.data.role === 'admin'" read: true delete: "user.data.role === 'admin'" listen: "user.data.role === 'admin'" notify: "user.data.role === 'admin'" "users/$owner/*": create: "user.id === $owner || user.data.role === 'admin'" write: "user.id === $owner || user.data.role === 'admin'" read: "user.id === $owner || user.data.role === 'admin'" delete: "user.id === $owner || user.data.role === 'admin'" listen: "user.data.role === 'admin'" notify: "user.data.role === 'admin'" event:So alice (role app) may write users/alice/... and read public/..., and everything else is denied. The output below shows alice logged in with the same client as in Step 6, after an admin had written public/notice:
write users/alice/profile -> ok read public/notice -> {"text":"maintenance at 22:00"} write public/notice -> MESSAGE_DENIED read demo/settings -> MESSAGE_DENIED rpc demo/add -> MESSAGE_DENIEDTo grant more, add a rule for the name prefix your application uses. The most specific matching pattern decides. For example, this rule adds a chat/* record prefix that every app user may read and write:
"chat/*": create: "user.data.role === 'app' || user.data.role === 'admin'" write: "user.data.role === 'app' || user.data.role === 'admin'" read: "user.data.role === 'app' || user.data.role === 'admin'" delete: "user.data.role === 'admin'" listen: "user.data.role === 'admin'" notify: "user.data.role === 'admin'"Edit the file with sudoedit /etc/deepstream/permissions.yml, then run sudo systemctl restart deepstream. Rules are deepstream Valve expressions. user.id is the user name and user.data is its serverData from users.yml. See the deepstream documentation for the data, oldData and path variables that rules can use.
Browser applications and the Origin guard
A browser sends an Origin header with every WebSocket upgrade. nginx lets an upgrade through when there is no Origin (a server-side or mobile client) or when it is one of this instance's own https:// origins. It refuses any other site with 403 before deepstream sees it:
H="$(sudo sed -n 's/^DEEPSTREAM_HOST=//p' /root/deepstream-credentials.txt)" ws() { curl -s -m 3 -o /dev/null -w '%{http_code}\n' --cacert /etc/sac/deepstream-tls.pem --http1.1 -H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' --resolve "$H:443:127.0.0.1" "$@" "https://$H/deepstream"; } echo "no Origin (a server-side client): $(ws)" echo "Origin https://$H (this instance): $(ws -H "Origin: https://$H")" echo "Origin https://evil.example (another site): $(ws -H 'Origin: https://evil.example')"no Origin (a server-side client): 101 Origin https://<public-ip> (this instance): 101 Origin https://evil.example (another site): 403The status 101 is the WebSocket upgrade. If your web application is served from its own site, allow its origin. The change is saved and applied at once:
sudo deepstream-allow-origin add https://app.example.com
sudo deepstream-allow-origin list
sudo deepstream-allow-origin remove https://app.example.comBrowsers do not let a page pin a certificate, so browser clients need a CA-issued certificate for a DNS name (Step 12).
What the front door refuses
nginx serves exactly two paths. deepstream itself matches its HTTP paths by prefix (/apiX and /api/x would reach its API), but nginx answers 404 for every other path. deepstream's health check is reachable on the instance only (http://127.0.0.1:6020/health-check):
cd ~/ds-demo for p in /health-check /apiX /api/x; do echo "$p -> $(curl -s -o /dev/null -w '%{http_code}' --cacert /etc/sac/deepstream-tls.pem -H 'Content-Type: application/json' --data-binary @write.json https://localhost$p)"; done echo "http://localhost/deepstream -> $(curl -s -o /dev/null -w '%{http_code} %{redirect_url}' http://localhost/deepstream)" echo "health check on the instance -> $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:6020/health-check)"/health-check -> 404 /apiX -> 404 /api/x -> 404 http://localhost/deepstream -> 301 https://<public-ip>/deepstream health check on the instance -> 200nginx also refuses the following:
- Requests for a host name the instance does not serve. The connection is dropped.
;path parameters, with 400.- Per client address, more than 60 new WebSocket connections a minute (a burst of 40 is allowed) or 120 HTTP API calls a minute (burst 60), with 429.
The limits are in /etc/nginx/sites-available/sac-deepstream (limit_req_zone). Review them if your clients connect through one shared address.
Use your own domain and a CA-issued certificate
Point a DNS name at the instance (an Elastic IP keeps the address stable), then make it the served name. This issues a new self-signed certificate for it:
sudo deepstream-set-hostname deepstream.example.comTo serve a certificate from your CA (or Let's Encrypt) for that name, which browsers and clients trust without pinning, install the chain, with the server certificate first, and its unencrypted private key:
sudo deepstream-install-cert /path/to/fullchain.pem /path/to/privkey.pem deepstream.example.comThe boot-time renewal never replaces a certificate you installed. Run sudo deepstream-set-hostname <name> to go back to a self-signed one. Both commands update the URLs and the fingerprint in the credentials file.
The instance metadata block
deepstream, nginx and the boot service may not connect to the EC2 instance metadata service (169.254.169.254) or its IPv6 and IPv4-mapped forms. Only a small boot unit reads the instance's own names and addresses for the certificate, before deepstream starts. Show the deny rules for each service:
for u in deepstream nginx deepstream-firstboot; do printf '%s: ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; donedeepstream: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 nginx: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 deepstream-firstboot: 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128The self-check in Step 14 proves this from inside deepstream's own service. A connection to the metadata service from there never completes, while a loopback connection does. deepstream also runs as the unprivileged deepstream user, cannot read /root, the TLS key or the boot state, and can write only its empty state directory.
Re-verify the appliance
deepstream-selfcheck re-checks the whole appliance: the units and files, the certificate, the front door (redirect, Origin guard, refusals, exact routes), the HTTP API, the official client over wss:// with the certificate pinned (the refusal matrix and a record, event, RPC and presence round trip), the listening set and the metadata block. It reads the admin password from the credentials file, never from a command line. Run it:
sudo deepstream-selfcheckok units active; credentials file, TLS key and deepstream config present with their permissions; the config gate accepts the configuration ok TLS: the served certificate is this instance's end-entity certificate (CA:FALSE, serverAuth; SHA-256 <fingerprint>...) ok front door: TLS only (80 -> 301), unknown Host dropped; WebSocket no-Origin 101, own Origin 101, foreign / null / http Origin 403 (before the upgrade), non-WebSocket 426, upgrade elsewhere 403; HTTP API anonymous / userA / wrong password 401 (deepstream), admin 200 (a record written, read back and deleted), foreign Origin 403, text/plain and form 415, GET / OPTIONS / PUT 405, ';' 400; exact routes only: /apiX /api/ /api/x /api/auth /deepstreamX /deepstream/ /health-check /health-checkX / 404; CORS stripped admin login accepted over wss://<public-ip>/deepstream (clientData role admin; positive control) anonymous refused: {"error":"missing authentication parameter: username or/and password"} missing password refused: {"error":"missing authentication parameter: username or/and password"} upstream sample userA/"password" refused: "INVALID_AUTHENTICATION_DETAILS" wrong password refused: "INVALID_AUTHENTICATION_DETAILS" unknown user refused: "INVALID_AUTHENTICATION_DETAILS" event: connection B -> deepstream -> connection A (nonce matched) record: update by B delivered to A's subscription (nonce matched) RPC: A called the procedure B provides and got B's answer presence: admin reported online ok client (@deepstream/client 7.1.1, certificate pinned for this connection): refusal matrix exact, admin login accepted; event, record, RPC and presence delivered between two connections ok public listeners 22/80/443 only; deepstream on 127.0.0.1:6020 only (health check answers there) ok from inside deepstream.service's cgroup: 169.254.169.254 unreachable (curl v4 and v4-mapped; Node.js timeout), loopback control 200 SELFCHECK OKLogs and service management
deepstream logs to the journal without colour codes. It logs connections and logins, and never the data a client logged in with:
sudo journalctl -u deepstream -n 6 -o cat --no-pagerCLIENT_DISCONNECTED | admin INCOMING_CONNECTION | from undefined (127.0.0.1) AUTH_SUCCESSFUL | admin CLIENT_DISCONNECTED | admin INCOMING_CONNECTION | from undefined (::1) CLIENT_DISCONNECTED | nullThe configuration is /etc/deepstream/config.yml. Before deepstream starts, deepstream-config-gate checks it. It refuses to start the server if the configuration would weaken the appliance, and the reason is in journalctl -u deepstream. The checks reject:
- Authentication other than hashed file authentication, or a user without a digest.
- A bind other than
127.0.0.1:6020, or another connection endpoint (text or JSON WebSocket, MQTT). - Logging of failed login data, the HTTP token endpoint, telemetry, or a plugin directory.
${...}environment expansion.
Restart the service and check its status with:
sudo systemctl restart deepstream
sudo systemctl status deepstreamSecurity notes
- Keep
/root/deepstream-credentials.txtsecret and give each application its own user with the narrowest role that works (Step 9). Theadminrole can read and change everything. - deepstream stores records in memory only on this image. Anything clients write is lost when deepstream restarts unless you configure a storage connector.
- Restrict the security group's port 443 to the networks your clients connect from, and port 22 to your admin network.
What changed since the Azure image (deepstream.io 10.1.4)
The SecureAnyCloud Azure Marketplace image of deepstream.io ships 10.1.4 behind plain HTTP on port 80 with md5 password digests at 100 iterations. This AWS image runs 10.2.4 (which fixes GHSA-rv32-v8hf-rxqg, GHSA-9r5v-rrr7-v476, GHSA-g4qj-f7jp-76c6, GHSA-v448-hpx6-fm45 and GHSA-j55p-v9rg-r9f2), serves only WSS and HTTPS, hashes with PBKDF2-SHA512 at 210,000 iterations, refuses foreign-origin WebSocket upgrades, gives non-admin users least privilege, and blocks the instance metadata service.
Support
SecureAnyCloud provides support for this image by email at sales@secureanycloud.com. deepstream.io is open source software under the MIT licence. This image is not affiliated with or endorsed by the deepstream.io project.
Deploy on AWS
Launch deepstream.io 10.2.4 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