Skip to content

Add a Block Creality Cloud Telemetry option - #18

Open
arlophoenix wants to merge 2 commits into
C0DEbrained:mainfrom
arlophoenix:feat/block-creality-cloud-telemetry
Open

Add a Block Creality Cloud Telemetry option#18
arlophoenix wants to merge 2 commits into
C0DEbrained:mainfrom
arlophoenix:feat/block-creality-cloud-telemetry

Conversation

@arlophoenix

@arlophoenix arlophoenix commented Jul 27, 2026

Copy link
Copy Markdown

Creality's stock telemetry agent uploads printer configs and Klipper logs to *.cxswyjy.com, a domain the crealitycloud.com-only blocklists in circulation do not cover at all. This adds a Customize menu option that blackholes the 16 known Creality cloud hosts and drops outbound MQTT to anything off the LAN.

The agent is alchemistp (init script CS61alchemistp_service), whose config at /etc/appetc/alchemistp/config.json declares UploadFileFlag: 1 over /usr/data/printer_data/config/* and /usr/data/printer_data/logs/*, pointed at c-smart.cxswyjy.com and devdata.cxswyjy.com. It has already uploaded: upload_file_offset.json on a stock machine lists printer.cfg, klippy.log and the per-daemon logs as having gone out. Captured on the wire mid-print on a K1C 2025, with a five-host .com blocklist already active:

14:24:22.489618 IP 192.168.0.76.34009 > 1.1.1.1.53: A? devdata.cxswyjy.com.
14:24:22.538055 IP 1.1.1.1.53 > 192.168.0.76.34009:
                CNAME devdata-1343527910.us-east-1.elb.amazonaws.com., A 98.86.129.225
14:24:22.539063 IP 192.168.0.76.48120 > 98.86.129.225.443: Flags [S]
14:24:22.743789 IP 98.86.129.225.443 > 192.168.0.76.48120: Flags [S.]

That SYN-ACK is a real AWS load balancer answering, and /proc/net/tcp showed the socket established a minute later. Separately, onyxp beacons SN and MAC to the .cn signalling hosts once per boot and got a live {"code":4,"errMsg":"Invalid Token"} reply, which likewise means a server answered rather than a blackhole.

The option installs the hosts entries and an init script at $INITD_FOLDER/S995cloudblock. The boot script is not optional bookkeeping: on the K1 2025 the rootfs is a volatile RAM filesystem, so /etc/hosts reverts to stock on every boot, and iptables rules never survive a reboot on any model.

Everything the script writes is explicitly owned rather than identified by matching content, because content matching means uninstall removes state it never created. The hosts entries live between # BEGIN and # END markers and are removed as a range, so a user who had already blackholed one of these hosts by hand keeps their line. The firewall rules live in a CREALITY_CLOUDBLOCK chain that uninstall flushes and deletes, so an identical MQTT rule belonging to another tool survives, and removal does not need to know which LAN was attached at install time. Both are rebuilt from scratch on every start, so a changed subnet, a hand-edited entry, or a host list that grew between versions self-heal instead of accumulating.

Two details are worth calling out because the obvious implementation gets them wrong. Loopback needs an explicit exemption ahead of the MQTT drops: with the hosts blackhole active a daemon resolves mqtt.crealitycloud.com to 0.0.0.0, the kernel routes that connect to 127.0.0.1:1883, and a plain --dport 1883 -j DROP swallows it silently instead of letting it fail fast with a RST. And the chain is linked into OUTPUT with an insert rather than an append, because an OUTPUT chain that already carries a broad ACCEPT would otherwise match first and the drops would never be reached. The LAN is detected from ip route rather than hardcoded, keyed on src plus a CIDR rather than on proto kernel which not every ip implementation prints, and the exemption is skipped entirely when detection turns up nothing, because a wrong CIDR on a headless printer is undebuggable remotely.

Failures are not swallowed. iptables calls take -w where the build supports it and fall back where it does not, a present-but-unusable iptables is distinguished from a model that simply has none, and start returns non-zero so the installer reports a partial apply instead of printing success over it.

Per-model behaviour is gated on capability, not assumption. The K1 and Ender-3 V3 series ship without an iptables binary even though the kernel module is loaded, so the port rules are skipped there and the install says so; the hosts blocklist still applies. INITD_FOLDER from paths.sh handles the /usr/apps/etc/init.d versus /etc/init.d split, and on the 2025 that path is what /bin/seed.sh walks for S??* at boot.

This is a blocklist, not a firewall. The OUTPUT policy is left untouched and nothing else is filtered, so OctoEverywhere, Obico, GuppyFLO, SimplyPrint, OctoApp and Mobileraker are unaffected.

Test plan

  • sh -n parses the new boot script, the new option, paths.sh, and all twelve touched menu files
  • Boot script exercised under dash against a stateful fake iptables (custom chains included) and a fake ip route: 16 entries applied, idempotent across repeated starts, the chain linked once and first in OUTPUT, and after uninstall no rules, no chain, and no residue
  • Ownership cases: a user's own 0.0.0.0 entry for a blocked host survives uninstall, another tool's identical --dport 1883 -j DROP and broad -o lo -j ACCEPT are left untouched, and a hand-edited managed line heals on the next start rather than duplicating
  • Degradation cases: no iptables at all, iptables present but rejecting -w, no route detected, and a subnet change between install and uninstall all behave correctly and leave nothing behind
  • /etc/hosts restored byte-for-byte on a newline-terminated file; on a file with no trailing newline it comes back with one synthesised newline added and is otherwise identical
  • Verified directly on a K1C 2025 and a K1C running the OverlayFS firmware: the exact sed range delete and its unterminated-block recovery, local in ash, tail -c, the ip route parse, iptables -w and -C support on the 2025 and the total absence of iptables on the OG, /etc/hosts being a regular file on both, and that rcK calls stop at shutdown
  • Menu rows, message box and Information line rendered locally; alignment matches the existing options
  • Manual test pass (see checklist below)

The shell-level checks above use fakes for iptables and ip route, so they verify the logic rather than the live effect. Nothing has been installed on a printer, no reboot persistence test has been run, and the per-model gating is reasoned rather than observed on four of the six models. The manual pass below is the one that matters and has not been done.

Manual test pass

  • Install through the Customize menu on a K1 2025; expect 16 entries between the markers in /etc/hosts and an executable S995cloudblock in $INITD_FOLDER
  • nslookup devdata.cxswyjy.com; expect 0.0.0.0
  • S995cloudblock status; expect 16 entries reported and the chain listed with the loopback and LAN ACCEPT rules above both drops
  • iptables -L OUTPUT -v -n; expect the jump to CREALITY_CLOUDBLOCK, and the 1883 drop counter not climbing steadily (steady growth means the loopback exemption is wrong)
  • Reboot; expect both the hosts entries and the chain to come back on their own
  • Uninstall; expect /etc/hosts identical to stock, no CREALITY_CLOUDBLOCK chain, and the init script gone
  • Confirm a print completes and Fluidd, Moonraker and the webcam are unaffected
  • Install and uninstall on a K1 or Ender-3 V3; expect the skipped-port-rules notice and a clean hosts restore

Not addressed

/etc/hosts cannot express wildcards, so any other subdomain of cxswyjy.com or crealitycloud.cn stays reachable, and a hardcoded IP bypasses the hosts file entirely. The port rules cover the hardcoded MQTT brokers in vectorp (47.114.48.45:1883 and 120.55.101.240:1883), but the wildcard gap is real. Closing it properly needs a default-deny egress firewall, which breaks every cloud relay option this helper ships, so that UX problem should be solved before such a thing is offered to anyone.

The LAN wait at boot is capped at five seconds. If DHCP takes longer than that on a cold wifi association, the chain is built without a LAN exemption and a local MQTT broker stays unreachable until the next start. Raising the cap would delay boot on a printer with no network at all, and a background retry felt like more machinery than this warrants, so the bound stays fixed and visible as LAN_WAIT_SECONDS.

Remove is gated on the init script existing. If someone deletes $INITD_FOLDER/S995cloudblock by hand, the hosts entries and the chain stay live with no menu path to clear them; recovery is to install again and then remove. On the K1 2025 a reboot clears both anyway, since the rootfs and the ruleset are volatile.

Disabling the stock daemons outright is deliberately separate. The two are complementary rather than alternatives: disabling removes the processes that want to talk, blocking removes the ability, and the block is what still protects a user after a firmware OTA or a helper reinstall puts the daemons back.

IPv6 is not filtered. The 2025 has no IPv6 address and no v6 default route, so v6 egress is currently impossible, and its kernel has no ip6 filter table at all, so if SLAAC were ever enabled there would be no way to filter it.

The single stale broker IP 47.253.214.226 that older blocklist scripts pin is deliberately not included. It appears in none of the seven stock binaries; it was a runtime DNS answer for mqtt.crealitycloud.com on one particular day and pinning it achieves nothing the hosts entry does not.

The stock telemetry agent (alchemistp) uploads printer configs and Klipper
logs to *.cxswyjy.com, which the crealitycloud.com-only blocklists in
circulation do not cover at all. This adds a menu option that blackholes the
16 known Creality cloud hosts in /etc/hosts and drops outbound MQTT on tcp
1883/8883 to anything off the LAN.

The rules are re-applied by an init script because the K1 2025 rootfs is a
volatile RAM filesystem, so /etc/hosts reverts to stock on every boot, and
iptables rules never survive a reboot on any model.

It is a blocklist, not a firewall: the OUTPUT policy is untouched and nothing
else is filtered, so the cloud relay options the helper installs keep working.
iptables is missing entirely on the K1 and Ender-3 V3 series, so the port
rules are skipped there and only the hosts blocklist is applied.
Review found that both halves of this option identified their own state by
matching content, so uninstall could remove things it never created. A user who
had already blackholed one of these hosts by hand lost that line, and an
identical MQTT rule belonging to another tool was deleted along with ours.

The hosts entries now live between BEGIN and END markers and are removed as a
range, and the firewall rules live in a CREALITY_CLOUDBLOCK chain that is
flushed and deleted on uninstall. Both are rebuilt from scratch on every start,
so a changed LAN, a hand-edited entry, or a host list that grew between versions
self-heal instead of accumulating. Removing the content matching also removed
the need to recompute the LAN at uninstall time, which used to strand ACCEPT
rules for a subnet the printer had moved off.

The chain is linked with an insert rather than an append, because an OUTPUT
chain that already carries a broad ACCEPT would otherwise match before the
drops and the block would silently do nothing.

Rule failures are no longer swallowed: iptables calls take -w where the build
supports it, a present-but-unusable iptables is now distinguished from a model
that has none, and start propagates a non-zero exit so the installer stops
reporting success after a partial apply.

Also pins umask 022 for the same reason S56moonraker_service does, stops
requiring "proto kernel" in ip route output, and keeps the guard against a
hosts file with no trailing newline, without which the begin marker would join
onto the last line and hide the block from the range delete.
@arlophoenix
arlophoenix marked this pull request as draft July 27, 2026 20:34
@arlophoenix
arlophoenix marked this pull request as ready for review July 27, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant