Eclipse Zenoh 1.10.2 on AWS User Guide
Overview
This guide covers launching and using the Eclipse Zenoh 1.10.2 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.
Eclipse Zenoh is a pub/sub, query and storage protocol for robotics, IoT, edge and cloud. Applications publish values under key expressions (paths such as fleet/robot-7/battery), subscribe with wildcards (fleet/**), answer queries with queryables and keep values in storages. This image runs zenohd, the Zenoh router: the infrastructure node your clients and peers connect to.
What the cloudimg image adds:
- TLS only. The router's only network listener is
tls/0.0.0.0:7447, and Zenoh's protocol whitelist accepts TLS and nothing else: plaintcp/, UDP, QUIC, WebSocket and unix-socket sessions are refused. (The upstream package listens on plaintcp/[::]:7447, where every sample, query and reply crosses the network unencrypted.) QUIC stays off. - A certificate made on your instance. At first boot the router gets a TLS key and an end-entity certificate for this instance's own names and addresses. It can only vouch for this router and can never sign other certificates. You pin it in your clients (Step 5). When the instance's addresses change (an EC2 stop and start gives a new public IP), the certificate is re-issued on the same key, so clients that pinned it keep connecting.
- Logins that are on, with no default account. Upstream's package accepts any client with no authentication. Every session here must log in with user/password (Zenoh's HMAC challenge, inside TLS). First boot creates two logins unique to your instance:
cloudimg, the administrator, andcloudimg-app, a least-privilege application identity. No password is ever placed on a command line. - Access control that defaults to deny.
cloudimgmay use every key expression and read the router's admin space.cloudimg-appmay usecloudimg/app/**and nothing else.sudo zenoh-user addcreates more scoped users. - No discovery traffic. Multicast and gossip scouting are off and the router connects to nothing on its own, so it never announces itself on your network or joins one by accident.
- The REST API on the loopback address. The REST plugin runs inside the router and is not subject to the access control, so it listens on
127.0.0.1:8000only. You reach it through an SSH tunnel (Step 10). - Fail closed. A configuration gate checks the router's effective configuration before every start and refuses to start it on an unsafe change (a plain-tcp listener, scouting on, access control off, the REST API off loopback, a short or published password, and more). A listener check stops the router if it ever listens beyond its configuration. The router runs as an unprivileged user in a systemd sandbox and cannot reach the instance metadata service.
- A pinned supply chain. zenohd, the REST and storage-manager plugins and the filesystem storage backend come from the official 1.10.2 release archives, each checked against its published SHA-256. The official
eclipse-zenoh1.10.2 Python client is installed from its hash-pinned wheel, with every file checked against the wheel's own record. 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: zenohd 1.10.2 with zenoh-plugin-rest, zenoh-plugin-storage-manager and zenoh-backend-filesystem 1.10.2 (from the Eclipse Zenoh release packages), the eclipse-zenoh 1.10.2 Python client in /usr/local/lib/cloudimg/zenoh-venv, the cloudimg helpers zenoh-probe, zenoh-user, zenoh-selfcheck, zenoh-set-hostname and zenoh-install-cert, cloud-init and the AWS Systems Manager agent. 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 7447 (Zenoh over TLS) from the networks your clients, sensors or other routers connect from. Nothing else needs to be open: the REST API (8000) listens on the loopback address only and refuses connections from other machines even if a security group allows them.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Eclipse Zenoh 1.10.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger for many sessions or high message rates), 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=zenoh-router}]'
The image requires IMDSv2 (session tokens). First boot takes under a minute after the instance is running: the boot service creates the logins and the certificate, then the router starts.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Eclipse Zenoh 1.10.2 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 zenoh-metadata-names zenohd-firstboot zenohd
sudo ss -tlnp | grep -E ':(22|7447|8000) '
sudo ss -ulnp | grep zenohd || echo 'no UDP socket'
active
active
active
LISTEN 0 128 127.0.0.1:8000 0.0.0.0:* users:(("zenohd",pid=1049,fd=12))
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1071,fd=3),("systemd",pid=1,fd=58))
LISTEN 0 1024 0.0.0.0:7447 0.0.0.0:* users:(("zenohd",pid=1049,fd=22))
LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=1071,fd=4),("systemd",pid=1,fd=59))
no UDP socket
zenoh-metadata-names reads this instance's EC2 names for the certificate, zenohd-firstboot prepares the logins and the certificate, and zenohd is the router: TLS on 7447 for everyone, the REST API on 127.0.0.1:8000 for this instance only, no UDP at all.
Step 4: Your endpoint, logins and client configurations
Everything generated for your instance is in /root/zenoh-credentials.txt (readable by root only). View it all with sudo cat /root/zenoh-credentials.txt; the non-secret lines are:
sudo grep -E '^ZENOH_(HOST|ENDPOINT|ADMIN_USER|APP_USER|APP_PREFIX|TLS_CERT_SHA256|TLS_PUBKEY_SHA256)=' /root/zenoh-credentials.txt
ZENOH_HOST=18.209.60.217
ZENOH_ENDPOINT=tls/18.209.60.217:7447
ZENOH_ADMIN_USER=cloudimg
ZENOH_APP_USER=cloudimg-app
ZENOH_APP_PREFIX=cloudimg/app
ZENOH_TLS_CERT_SHA256=37:D8:1C:44:AF:06:7A:DB:73:93:20:94:8F:24:36:1E:E2:4E:42:44:66:07:F2:B4:15:07:1A:49:65:74:81:4A
ZENOH_TLS_PUBKEY_SHA256=e0fd471e8065003514ad528a0519bc1653932033ddda1fc9bdc90b1fc669abd0
The file also holds ZENOH_ADMIN_PASSWORD and ZENOH_APP_PASSWORD. First boot also wrote two ready-to-use Zenoh client configurations (root only, mode 0600): /root/zenoh-client-admin.json5 for the administrator and /root/zenoh-client-app.json5 for the application identity. Each holds the endpoint, the certificate to pin and the login, so your client code reads the password from a file and never from its command line. Here is the application one with its password masked:
sudo sed -E 's/password: "[^"]*"/password: "<app-password>"/' /root/zenoh-client-app.json5
// Zenoh client configuration for the application identity (cloudimg; generated on this instance at first boot).
// Allowed keys: cloudimg/app/**. On another machine, copy /etc/zenohd/tls/zenohd.crt there and set
// root_ca_certificate to its path (this pins the router certificate). Keep this file secret: it holds the password.
{
mode: "client",
connect: { endpoints: ["tls/18.209.60.217:7447"] },
scouting: { multicast: { enabled: false, listen: false }, gossip: { enabled: false } },
transport: {
link: { tls: { root_ca_certificate: "/etc/zenohd/tls/zenohd.crt" } },
auth: { usrpwd: { user: "cloudimg-app", password: "<app-password>" } },
},
}
Give applications the cloudimg-app login or a scoped user (Step 8), not the administrator's.
Step 5: Check the certificate and pin it in your client
The router's certificate is self-signed, made on your instance:
openssl x509 -in /etc/zenohd/tls/zenohd.crt -noout -subject -fingerprint -sha256 -ext subjectAltName,basicConstraints
openssl s_client -connect 127.0.0.1:7447 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256
subject=CN = zenoh-router, O = cloudimg
sha256 Fingerprint=37:D8:1C:44:AF:06:7A:DB:73:93:20:94:8F:24:36:1E:E2:4E:42:44:66:07:F2:B4:15:07:1A:49:65:74:81:4A
X509v3 Subject Alternative Name:
IP Address:18.209.60.217, DNS:ec2-18-209-60-217.compute-1.amazonaws.com, IP Address:172.31.87.224, DNS:ip-172-31-87-224.ec2.internal, IP Address:127.0.0.1, DNS:localhost
X509v3 Basic Constraints: critical
CA:FALSE
sha256 Fingerprint=37:D8:1C:44:AF:06:7A:DB:73:93:20:94:8F:24:36:1E:E2:4E:42:44:66:07:F2:B4:15:07:1A:49:65:74:81:4A
On each client machine, copy the certificate and compare what the router serves with ZENOH_TLS_CERT_SHA256 before you trust it:
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/zenohd/tls/zenohd.crt ./zenohd.crt
openssl s_client -connect <public-ip>:7447 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256
Then pin it in that client's own Zenoh configuration: set transport.link.tls.root_ca_certificate to the path of the copied zenohd.crt. This makes the client accept this router's certificate for this connection only. Do not add the certificate to your operating system's or browser's trust store. A client without the pin is refused (invalid peer certificate: UnknownIssuer), and the router's name or address must be one of the certificate's names (the client checks it).
The certificate is re-issued on the same key and subject when the instance's addresses change, so its SHA-256 fingerprint changes but ZENOH_TLS_PUBKEY_SHA256 stays the same, and a client that pinned the original zenohd.crt keeps connecting. To use a DNS name, see Step 11.
Step 6: Connect a client from another machine
Copy the application client configuration and the certificate to the client machine (keep the configuration file private: it holds the password), then point root_ca_certificate at the copied certificate:
ssh -i <your-key.pem> ubuntu@<public-ip> sudo cat /root/zenoh-client-app.json5 > zenoh-client-app.json5
chmod 600 zenoh-client-app.json5
sed -i "s#/etc/zenohd/tls/zenohd.crt#$PWD/zenohd.crt#" zenoh-client-app.json5 # on macOS: sed -i ''
Python (3.9 or later): install the official client of the same release, python3 -m venv zenoh-env && zenoh-env/bin/pip install eclipse-zenoh==1.10.2, then save this as zenoh_demo.py:
# zenoh_demo.py - publish, subscribe and query through your cloudimg Eclipse Zenoh router.
# Usage: python3 zenoh_demo.py zenoh-client-app.json5
# The client configuration file holds the endpoint (tls/<router>:7447), the certificate to pin and the login,
# so no password ever appears on the command line.
import sys
import time
import zenoh
zenoh.init_log_from_env_or("error")
conf = zenoh.Config.from_file(sys.argv[1])
with zenoh.open(conf) as session:
received = []
sub = session.declare_subscriber("cloudimg/app/demo/**",
lambda s: received.append((str(s.key_expr), s.payload.to_string())))
qbl = session.declare_queryable("cloudimg/app/demo/status",
lambda q: q.reply(q.key_expr, "ready"))
time.sleep(1)
session.put("cloudimg/app/demo/temperature", "21.5")
time.sleep(1)
for key, value in received:
print("received", key, "=", value)
for reply in session.get("cloudimg/app/demo/status"):
if reply.ok is not None:
print("reply ", reply.ok.key_expr, "=", reply.ok.payload.to_string())
zenoh-env/bin/python zenoh_demo.py zenoh-client-app.json5
received cloudimg/app/demo/temperature = 21.5
reply cloudimg/app/demo/status = ready
(This output is from a client on a second EC2 instance connecting to the router over TLS with the cloudimg-app login and the pinned certificate.)
Rust: add zenoh = "1.10.2" and tokio (with the macros and rt-multi-thread features) to Cargo.toml and load the same file:
#[tokio::main]
async fn main() {
let config = zenoh::Config::from_file("zenoh-client-app.json5").unwrap();
let session = zenoh::open(config).await.unwrap();
session.put("cloudimg/app/demo/from-rust", "hello").await.unwrap();
let replies = session.get("cloudimg/app/demo/status").await.unwrap();
while let Ok(reply) = replies.recv_async().await {
if let Ok(sample) = reply.result() {
println!("{} = {}", sample.key_expr().as_str(), sample.payload().try_to_string().unwrap());
}
}
}
The upstream Zenoh examples take the same file with -c, for example cargo run --example z_sub -- -c zenoh-client-app.json5 -k 'cloudimg/app/**' in a checkout of the zenoh 1.10.2 repository.
Keep the client's log level at error, warn or info: at RUST_LOG=debug Zenoh clients log their whole configuration, the password included.
Step 7: Pub/sub, queries and storage on the instance
zenoh-probe is a small client on the image (run with sudo so it can read the credentials file; --identity admin or --identity app picks the login). Each command is its own Zenoh session, so these are two independent clients talking through the router:
sudo zenoh-probe subscribe --identity app --key 'cloudimg/app/demo/**' --count 1 --duration 10 & sleep 2
sudo zenoh-probe publish --identity app --key cloudimg/app/demo/temperature --payload 21.5
wait
SUBSCRIBER_READY
SAMPLE cloudimg/app/demo/temperature 21.5
RECEIVED_COUNT 1
PUBLISHED cloudimg/app/demo/temperature
A queryable answering a get:
sudo zenoh-probe queryable --identity app --key cloudimg/app/demo/status --payload ready --duration 6 & sleep 2
sudo zenoh-probe get --identity admin --key cloudimg/app/demo/status
wait
QUERYABLE_READY
REPLY cloudimg/app/demo/status ready
REPLY_COUNT 1
QUERYABLE_DONE
Storage. The storage manager keeps everything published under cloudimg/store/** on disk (the filesystem backend, under /var/lib/zenohd/storage), so it survives a router restart or a reboot; a get on the key answers from the storage:
sudo zenoh-probe publish --identity admin --key cloudimg/store/demo/greeting --payload 'hello from cloudimg'
sudo zenoh-probe get --identity admin --key 'cloudimg/store/demo/**'
PUBLISHED cloudimg/store/demo/greeting
REPLY cloudimg/store/demo/greeting hello from cloudimg
REPLY_COUNT 1
The storage directory ships empty. Size the instance's EBS volume for the data you keep there. The storage is reachable by the administrator only until you give a user access to cloudimg/store/** (Step 8).
Step 8: Access control, logins and scoped users
The router refuses a session without credentials, and a wrong password:
sudo zenoh-probe session --identity anonymous
2026-10-29T21:58:27.725050Z ERROR ThreadId(04) zenoh_transport::unicast::establishment::open: Received a close message (reason 0) in response to an InitSyn on: tls/127.0.0.1:50512 => tls/127.0.0.1:7447 at ...
SESSION_REFUSED Unable to connect to any of [Single(tls/127.0.0.1:7447)]! at ...
A wrong password is closed one step later, in response to an OpenSyn. The test logins that Eclipse Zenoh publishes in its own source (for example routername/routerpasswd) are refused too, and the router will not even start with one of them in its dictionary.
What a login may do is decided by the access_control section of /etc/zenohd/zenohd.json5, which defaults to deny:
| Login | May use |
|---|---|
cloudimg |
every key expression (**), and reading the router admin space (@/**) |
cloudimg-app |
cloudimg/app/** only |
A message outside a login's keys is dropped by the router. Here cloudimg-app publishes twice and the administrator, subscribed to cloudimg/**, receives only the allowed one:
sudo zenoh-probe subscribe --identity admin --key 'cloudimg/**' --duration 6 & sleep 2
sudo zenoh-probe publish --identity app --key cloudimg/private/x --payload denied
sudo zenoh-probe publish --identity app --key cloudimg/app/x --payload allowed
wait
SUBSCRIBER_READY
PUBLISHED cloudimg/private/x
PUBLISHED cloudimg/app/x
SAMPLE cloudimg/app/x allowed
RECEIVED_COUNT 1
Scoped users. zenoh-user adds a user with its own password (generated on the instance) and a rule allowing one key prefix, writes that user's ready-to-use client configuration to /root/zenoh-user-<name>.json5 and restarts the router (sessions reconnect). The password is never printed:
sudo zenoh-user add sensor-17 fleet/sensors/17
sudo zenoh-user list
zenoh-user: added sensor-17 (keys fleet/sensors/17/**); its client configuration, password included, is /root/zenoh-user-sensor-17.json5
cloudimg every key (administrator)
cloudimg-app cloudimg/app/**
sensor-17 fleet/sensors/17/**
sensor-17 may now use fleet/sensors/17/** and nothing else. Remove a user with sudo zenoh-user remove sensor-17. To give a login more than one prefix, edit the rule zenoh-user added in /etc/zenohd/zenohd.json5 (a line ending // zenoh-user:<name>) and sudo systemctl restart zenohd; the configuration gate checks the file before the router starts.
Step 9: The admin space
The router's admin space (@/<router-id>/...) describes the router, its plugins and its sessions. It is read-only, and only the administrator may read it:
sudo zenoh-probe get --identity admin --key '@/*/router' | cut -c1-200
REPLY @/d9074bfd276bd6c55a57f20d1a024b62/router {"locators":["tls/172.31.87.224:7447"],"metadata":null,"plugins":{"rest":{"name":"rest","path":"/usr/lib/libzenoh_plugin_rest.so"},"storage_manager":{"n
REPLY_COUNT 1
cloudimg-app receives nothing from it.
Step 10: The REST API (through an SSH tunnel)
The REST plugin maps HTTP to Zenoh: GET /<key-expr> is a Zenoh get (it can reach storages and queryables), PUT /<key> publishes and DELETE /<key> deletes. It runs inside the router and is not subject to the access control, which is why it listens on 127.0.0.1:8000 only. On the instance:
curl -s 'http://127.0.0.1:8000/cloudimg/store/demo/**'; echo
[{"key":"cloudimg/store/demo/greeting","value":"aGVsbG8gZnJvbSBjbG91ZGltZw==","encoding":"zenoh/bytes","timestamp":"7702212529998757696/d9074bfd276bd6c55a57f20d1a024b62"}]
(value is base64.) From your workstation, open a tunnel and use http://127.0.0.1:8000 there:
ssh -i <your-key.pem> -N -L 8000:127.0.0.1:8000 ubuntu@<public-ip>
Anyone with a shell on the instance can use the REST API without a Zenoh login, so keep shell access to people who may administer the router. Do not change http_port to a public address: the configuration gate refuses it.
Step 11: Use your own name or a CA-issued certificate
If clients reach the router by a DNS name (or an Elastic IP), add it to the certificate. The key stays the same, so existing pins keep working:
sudo zenoh-set-hostname zenoh.example.com
zenoh-set-hostname: clients connect to tls/zenoh.example.com:7447
zenoh-set-hostname: certificate SHA-256 69:E6:C8:C4:4B:19:C9:03:21:99:A8:E8:65:71:35:1D:21:CE:9F:D4:F0:F5:D6:2D:0E:C3:E3:48:A4:11:A8:54; public key SHA-256 e0fd471e8065003514ad528a0519bc1653932033ddda1fc9bdc90b1fc669abd0
It updates the endpoint in /root/zenoh-credentials.txt and in the client configurations, and restarts the router.
To use a certificate from your own CA (or a public CA), install the chain (server certificate first) and its unencrypted key:
sudo zenoh-install-cert /path/to/chain.pem /path/to/key.pem zenoh.example.com
Clients then set root_ca_certificate to your CA's certificate (or leave it unset for a certificate from a public CA). The boot-time renewal never replaces an installed certificate; sudo zenoh-set-hostname <name> goes back to a new self-signed one.
Mutual TLS (optional). To require a client certificate as well as the login, copy the certificate of the CA that signs your client certificates to /etc/zenohd/tls/client-ca.crt, then in the router's transport.link.tls section of /etc/zenohd/zenohd.json5 change enable_mtls: false to true and add the CA:
enable_mtls: true,
root_ca_certificate: "/etc/zenohd/tls/client-ca.crt",
Give each client enable_mtls: true, connect_certificate and connect_private_key in its own transport.link.tls section (next to its root_ca_certificate pin), and restart with sudo systemctl restart zenohd. A client without a certificate from that CA is then refused even with a valid login. Access-control subjects can also match cert_common_names.
Connecting other routers to this one. Another Zenoh router (or peer) joins this one the same way a client does: create it a scoped login with sudo zenoh-user add, and in that router's own configuration add tls/<this-host>:7447 to its connect.endpoints, the login under transport.auth.usrpwd and the pinned certificate under transport.link.tls.root_ca_certificate. Its traffic is then limited to the login's key prefix. This router does not dial out by default: connect.endpoints is empty, it accepts TLS endpoints only, and the configuration gate refuses a user/password in the router's own configuration, so links to a remote router that itself requires a login are made from that remote router's side.
Step 12: Why scouting is off, and the metadata block
Zenoh's default multicast scouting makes a router answer, and connect to, anything that announces itself on the local network, and gossip scouting propagates what routers learn about each other. On a cloud network that means joining or exposing a bus you did not plan. Both are off here and the router connects to nothing unless you list it in connect.endpoints. Clients find the router by its endpoint.
The router and its boot service cannot reach the EC2 instance metadata service, where instance-role credentials live, so even a compromised router cannot read them. Only the small zenoh-metadata-names service may, and it reads nothing but this instance's names and addresses for the certificate:
systemctl show -p IPAddressDeny zenohd zenohd-firstboot
IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 ::ffff:169.254.169.254/128 168.63.129.16/32 ::ffff:168.63.129.16/128
IPAddressDeny=::ffff:169.254.169.254/128 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 fd00:ec2::254/128
Step 13: Re-verify the appliance
zenoh-selfcheck proves pub/sub, queries, storage across a router restart, every refusal, the access control, the listening set, the metadata block and the certificate on your instance (it restarts the router once):
sudo zenoh-selfcheck
ok pub/sub: cloudimg-app published, a separate administrator subscriber received it byte for byte; a non-matching subscriber received nothing
ok query: a get from one process was answered by a queryable in another
ok storage: cloudimg/store/** is written to disk (filesystem backend) and survived a router restart
ok refused: no credentials (closed at InitSyn)
ok refused: wrong passwords and Eclipse Zenoh's published test logins (routername/routerpasswd, client1name/client1passwd, client2name/client2passwd, cloudimg/routerpasswd) closed at OpenSyn
ok refused: plain tcp to 7447; TLS without the pinned certificate (invalid peer certificate: UnknownIssuer)
ok control: the administrator and cloudimg-app logins open TLS sessions right after the refusals
ok access control: cloudimg-app was denied put, subscribe and get outside cloudimg/app/** and the router admin space @/ce3de20f.../** (0 deliveries / 0 replies), in the same run as its allowed publish and the administrator's deliveries and replies
ok listeners: zenohd.service listens exactly on 0.0.0.0:7447 127.0.0.1:8000 (tcp) and holds no udp socket; REST answers on 127.0.0.1:8000 and is refused on 172.31.87.224:8000 while 172.31.87.224:7447 opens a TLS session
ok metadata: 169.254.169.254 (v4 and v4-mapped) and 168.63.129.16 unreachable from inside zenohd.service; loopback control 200
ok certificate: 7447 serves /etc/zenohd/tls/zenohd.crt (SHA-256 37:D8:1C:44:AF:06:7A:DB...), an end-entity server certificate, as recorded in the credentials file
SELFCHECK OK
Step 14: Logs and service management
The router logs to the journal:
sudo journalctl -u zenohd -b -o cat --no-pager | grep -E 'zenohd v|config-gate|reached at|assert-listeners' | tail -4
zenohd-config-gate: OK (TLS-only listeners, user/password auth, access control default deny, scouting off, REST on loopback)
2026-10-29T21:57:13.909399Z INFO main ThreadId(01) zenohd: zenohd v1.10.2 built with rustc 1.97.1 (8bab26f4f 2026-07-14)
2026-10-29T21:57:17.419591Z INFO main ThreadId(01) zenoh::net::runtime::orchestrator: Zenoh can be reached at: tls/172.31.87.224:7447
zenoh-assert-listeners: zenohd.service listens exactly on 0.0.0.0:7447 127.0.0.1:8000 (tcp) and holds no udp socket
If the router refuses to start after you edit /etc/zenohd/zenohd.json5, journalctl -u zenohd shows the gate's reason (zenohd-config-gate: REFUSED: ...). The router runs as the unprivileged zenohd user and may write only its storage directory:
systemctl show -p User,NoNewPrivileges,ProtectSystem,ReadWritePaths zenohd
User=zenohd
ReadWritePaths=/var/lib/zenohd/storage
ProtectSystem=strict
NoNewPrivileges=yes
Security notes
- Keep
/root/zenoh-credentials.txtand the client configuration files secret; give each application or device its own scoped user (Step 8), not the administrator login. - Restrict the security group's port 7447 to the networks your clients connect from, and port 22 to your admin network.
- The REST API is unauthenticated and bypasses the access control by design (it runs inside the router); it is on the loopback address for that reason (Step 10).
- Run clients at log level
error,warnorinfo; atdebugthey log their configuration, password included.
What changed since the Azure image (Eclipse Zenoh 1.9.0)
The cloudimg Azure Marketplace image of Eclipse Zenoh ships 1.9.0 with user/password logins and access control, but its router listens on plain tcp/0.0.0.0:7447, so every sample, query and reply crosses the network unencrypted. This AWS image runs 1.10.2 (which fixes a QUIC listener denial of service; QUIC stays off here), accepts TLS sessions only with a per-instance certificate, turns gossip scouting off too, makes the admin space readable by the administrator only, checks the configuration before every start and blocks the instance metadata service.
Support
cloudimg provides support for this image by email at support@cloudimg.co.uk. Eclipse Zenoh is open source software under the Eclipse Public License 2.0 or the Apache License 2.0; this image is not affiliated with or endorsed by the Eclipse Foundation or the Eclipse Zenoh project.