A 3D printer running an end-of-life operating system is a good candidate for a network of its own with no way out. That part went fine, at three independent layers. Then the vendor's slicer, running on the workstation, popped an update prompt and loaded a web page. The isolation was correct and the application was never inside it.
For the appliance itself, defence in depth is cheap here, and each layer fails differently:
# NetworkManager
ipv4.never-default yes
ipv6.never-default yes
# netplan, for a DHCP interface on that VLAN
dhcp4-overrides: { use-routes: false, use-dns: false }
The result you want is an interface whose entire routing table is the local subnet and nothing else.
ip route on any multi-homed host and confirm exactly one default route exists.
Here is the part that is easy to get wrong, and it is worth stating plainly.
Putting a NIC on an isolated VLAN controls what is reachable on that subnet. It does nothing to the application. A normal unsandboxed process reads the whole routing table, so anything it sends that is not addressed to the isolated subnet, an update check, telemetry, a documentation link, simply leaves by the interface that does have internet, exactly like every other program on the machine.
ip route
# default via 192.168.1.1 dev enp17s0 <-- the slicer's update check went here
# 192.168.9.0/24 dev enp18s0 scope link <-- only printer-addressed traffic
So the network work was right, and it answered a different question than the one being asked. Isolating the device is a network problem. Confining the application is a host problem.
Move the interface into its own network namespace and start the app inside it. Once moved, the interface no longer exists in the root namespace, and the namespace contains no interface capable of reaching anything else:
ip netns add printer
ip link set enp18s0 netns printer
ip -n printer addr add 192.0.2.15/24 dev enp18s0
ip -n printer link set enp18s0 up # no default route, deliberately
# launch the app inside it
ip netns exec printer runuser -u you -- /usr/bin/TheSlicer
Four details that turn that into something that survives a reboot:
unmanaged-devices=interface-name:enp18s0 in a drop-in.ip netns exec grant is root-equivalent, because you can exec anything with it. Grant the one
script, and keep the display variables with env_keep.~/.local/share/applications beats the packaged one, so the launcher survives package
upgrades.The GUI is unaffected, because only the network namespace changes. Mount and IPC namespaces are shared, so the display socket still works.
Isolation that works cuts off your own tooling, and it is better to plan that than to discover it. Our monitoring and backup servers sit on the main VLAN and can no longer reach the printer at all.
Route both through the one host that has a foot in both networks:
ProxyJump, so the backup server
never needs a path of its own.