Skip to content

battalion-energy/mxview-container

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MXview One containerization

A reproducible, security-isolated Podman container build of Moxa MXview One 1.7.1 on Ubuntu 22.04.

What this is: a Containerfile, a set of host-side deployment scripts, a small handful of shell wrappers that work around vendor bugs, and a structured writeup of everything that's broken in the vendor's packaged Linux install and what we did about it.

What this is NOT: a redistribution of Moxa's software. The vendor zip (with the two .deb packages inside) is downloaded fresh from Moxa's CDN each time you build the image. The Containerfile never bakes the vendor binaries into the source tree and the .gitignore blocks them from ever being committed.

Why this exists

MXview One's documented Linux installation procedure does not produce a working installation on a clean Ubuntu 22.04 system — the .deb installs, but clicking Start in the control panel fails on a chain of packaging and runtime issues spanning the install guide, the postinst script, and the product runtime itself. We documented eleven distinct issues that have to be worked around to get the stack running. Each one is described in VENDOR_ISSUES.md with symptoms, root cause, and a suggested upstream fix, in a form intended to be useful to Moxa's engineering team.

The Containerfile comments reference those issue numbers at every workaround (e.g. # Workaround for VENDOR_ISSUES.md Issue 7: ...) so that a future reader can immediately understand the why behind any step that looks unusual.

This writeup is being shared with Moxa technical support alongside this repository. It is not a complaint — it's a reproducible bug report with attached workarounds, and we hope Moxa will use it that way.

One issue that matters even if you're not containerizing

VENDOR_ISSUES.md Issue 10 describes a polling-engine SEGV that triggers when MXview enumerates non-ethernet network interfaces — Tailscale, WireGuard, Docker bridges, inactive WiFi, and similar tunnel/bridge drivers. This affects bare-metal MXview One installs on Linux systems with those interface types present at startup, not just containerized deployments. Device Discovery silently returns zero devices because the polling engine exits before sending any probes, and there is no in-product error surface for the condition.

If you're evaluating MXview One on a workstation and Device Discovery returns nothing, it's worth checking whether you have Tailscale, Docker, WireGuard, or a similar service running. In our testing, temporarily stopping those services (or deploying MXview in an isolated netns, which is what this repository does) allowed the polling engine to start cleanly. Running strace on the polling engine was how we traced the root cause; an in-product diagnostic for this condition would save future customers the same investigation.

Target deployment

MXview One will typically run co-located on a shared industrial PC that is also running other workloads (e.g. an EMS stack on a plant control PC). This is the single most important design constraint and it shapes every networking and security decision below:

  • A bug in the vendor software must not be able to reach other workloads on the same host.
  • The container must not have host root, even if something inside escalates — hence --userns=auto.
  • The container must not have unrestricted network access to the host — hence rootful Podman with a macvlan network giving the container its own L2-adjacent position on the LAN.
  • Administrative access to the web UI is expected to happen via ssh -L port forwarding from an operator workstation; the host-side sibling macvlan interface (mxview-host0) exists specifically so that sshd on the host can reach the container.

If you are deploying this on a dedicated single-purpose NMS box, the same setup still works — you're just spending isolation budget you don't need.

Architecture at a glance

  +---------------------------------- host OS ----------------------------------+
  |                                                                             |
  |   enp68s0 (parent NIC)                                                      |
  |    ├── (host main stack, whatever else runs on the EMS IPC)                 |
  |    │                                                                        |
  |    ├── mxview-host0 (macvlan sibling, e.g. 192.168.0.251)                   |
  |    │     └── /32 route to container IP — lets sshd port-forwards reach     |
  |    │       the container, and lets the host browser test the UI            |
  |    │                                                                        |
  |    └── podman macvlan network "mxview-lan" (rootful)                        |
  |          └── container netns                                                |
  |                ├── lo                                                       |
  |                ├── eth0 (macvlan child, e.g. 192.168.0.250)                 |
  |                └── systemd pid 1                                            |
  |                      ├── MXControlPanel on :7100                            |
  |                      ├── MXviewOneNext (Caddy, mochi-mqtt, ...) on :8443    |
  |                      ├── MXviewPlatform (generic-processor)                 |
  |                      ├── mx-gateway-ng                                      |
  |                      ├── polling engine (SNMP/ICMP scanner)                 |
  |                      └── bundled PostgreSQL 15 on :5432 (loopback only)     |
  |                                                                             |
  +-----------------------------------------------------------------------------+

Repository layout

Containerfile                 — the image build. Heavily commented, every
                                vendor-bug workaround cross-referenced
                                to VENDOR_ISSUES.md.
VENDOR_ISSUES.md              — the full bug report: 11 issues, symptoms,
                                root cause, suggested vendor fixes, our
                                workarounds. Also intended to be sent to
                                Moxa support.
README.md                     — this file.
LICENSE                       — MIT license for the original work in this
                                repository.
NOTICE                        — nominative-use note for the Moxa and
                                MXview One trademarks.
scripts/
  env.example                 — site-specific deployment parameters. Copy to
                                scripts/.env and edit for your host.
  lib.sh                      — shared env loader and helpers.
  userns-setup.sh             — one-time: create the `containers` user and
                                allocate subuid/subgid ranges for
                                --userns=auto.
  host-network-setup.sh       — one-time per boot: create the mxview-host0
                                macvlan sibling and /32 route.
  host-network-teardown.sh    — cleanup for the above.
  podman-network-setup.sh     — one-time: create the rootful podman
                                macvlan network.
  build.sh                    — build the container image.
  run.sh                      — start (or restart) the container.
  stop.sh                     — stop and remove the container.
vendor-workarounds/
  initdb-wrapper.sh           — wraps bundled initdb to prepend narrowly
                                scoped loopback trust to pg_hba.conf
                                (see Issue 8).
  mxviewonenext-wrapper.sh    — wraps MXviewOneNext to ensure the stub
                                users table exists (see Issue 8).

Deployment

The setup scripts (userns-setup.sh, host-network-setup.sh, podman-network-setup.sh) are idempotent — safe to re-run.

run.sh is destructive. It stops and removes the existing container before launching a new one. Until persistent volumes are wired up (see Roadmap), this means every re-run loses the Postgres cluster, discovered devices, the admin password change, license activation, and any other runtime state. Plan accordingly. For day-to-day development sudo podman restart mxview is the non-destructive option; run.sh is for "rebuild and start fresh."

0. One-time host prerequisites

You need rootful Podman installed. On Debian/Ubuntu:

sudo apt install -y podman

Verify it works for rootful:

sudo podman version

1. Configure the site

cd ~/code/mxview
cp scripts/env.example scripts/.env
$EDITOR scripts/.env

Fill in:

  • MXVIEW_PARENT_IF — the host NIC attached to the LAN you want to monitor
  • MXVIEW_SUBNET, MXVIEW_GATEWAY — describing that LAN
  • MXVIEW_CONTAINER_IP — the IP the container will claim. Must be outside any DHCP pool on the LAN.
  • MXVIEW_HOST_SIBLING_IP — the host's own macvlan sibling IP, used for SSH port forwarding and local browser access

2. One-time host setup

sudo scripts/userns-setup.sh          # creates 'containers' user + subuid/subgid
sudo scripts/host-network-setup.sh    # creates mxview-host0 + route
sudo scripts/podman-network-setup.sh  # creates the rootful podman network

3. Build the image

sudo scripts/build.sh

Takes ~3 minutes on a first build (the vendor zip is ~560 MB, downloaded fresh from Moxa's CDN). Subsequent builds use cached layers.

4. Run the container

sudo scripts/run.sh

The script prints the two URLs you want:

Control Panel: https://<MXVIEW_CONTAINER_IP>:7100   (MXview admin console)
MXview One UI: https://<MXVIEW_CONTAINER_IP>:8443   (the actual product)

Both use a self-signed cert from the .deb — your browser will complain the first time. Default credentials on first login: admin / moxa. MXview One forces a password change on first login.

5. First-time application setup inside MXview One

The packaged .deb boots into a first-run flow:

  1. Accept the EULA
  2. Change the default password
  3. Activate the 90-day trial license (or load a purchased license)
  4. Start a Device Discovery scan against your LAN subnet

If device discovery returns devices that match what you can verify with snmpget from the host, the containerization is working end-to-end.

What works

  • Full MXview One web UI on 8443
  • Control Panel on 7100
  • Bundled PostgreSQL 15, correctly initialized and migrated
  • All MXview microservices (generic-processor, mx-gateway-ng, startMXsecurity, MXviewPlatform, mochi-mqtt, polling engine)
  • SNMP v1/v2c unicast device discovery
  • Generic SNMP polling (sysDescr, sysName, sysUpTime, sysObjectID, ifTable/ifXTable traffic counters, availability tracking)
  • ICMP reachability scans for non-SNMP LAN hosts
  • Moxa-specific device templates for Moxa hardware (not tested here because we don't have any)

What doesn't work in this deployment model (and why)

  • IP Conflict Detection (Moxa's libpcap-based feature). Requires passive packet capture on the physical LAN, which a macvlan-isolated container cannot do. To enable it you'd give up isolation entirely (--network host, --privileged, promiscuous mode) — a non-starter on a shared EMS IPC.
  • Subnet-broadcast auto-discovery. Only scan-range / unicast discovery is supported. MXview One doesn't rely on broadcast for documented discovery modes, so this is not in practice a regression.
  • SNMP traps on UDP 162 (receiving unsolicited device traps). The container has CAP_NET_BIND_SERVICE and its own LAN IP via macvlan, so it can in principle bind UDP 162 natively. What's missing is the MXview-side listener configuration — we haven't pointed MXview at UDP 162 on the container's IP yet. That's future work, not a fundamental blocker. See the "Roadmap" section below.

What's fragile / open questions

Documented in detail in VENDOR_ISSUES.md:

  • The mxview-core.migrate-mxview-user-log migration is an upgrade-era step with no fresh-install branch. We suppress the error with a stub table, but a future vendor release could change what the migration expects and break our stub. If so, the error message will be loud and easy to re-diagnose.
  • The bundled mosquitto broker may or may not be on any runtime- critical path; we haven't fully verified. We apply the cert-sync workaround defensively.
  • The polling engine is fragile to unusual interface types. Any deployment that exposes more than lo + one ethX to the container risks a SEGV. Macvlan happens to give us a minimal clean netns.

Security posture

  • Rootful Podman — required for macvlan on a physical NIC.
  • --userns=auto — container UID 0 is mapped to an unprivileged high UID on the host. A container escape does not immediately become host root.
  • --security-opt=no-new-privileges — setuid binaries inside the image cannot escalate.
  • --cap-add=NET_RAW — needed for ICMP ping sweeps.
  • --cap-add=NET_BIND_SERVICE — needed for future SNMP trap listener on UDP 162.
  • No --privileged. Ever, in production.
  • Macvlan network isolation — honestly described. The container has its own network namespace. It cannot reach the host's 127.0.0.1, and it cannot reach the parent NIC's own IP (e.g. enp68s0 at 192.168.0.106) — the macvlan driver blocks parent/child self-loops, which is the same kernel behavior that forced us to create the sibling interface in the first place. However, the container can reach the host via the sibling mxview-host0 interface (by design — this is what lets sshd port-forwarding terminate into the container). That means any host service bound on 0.0.0.0 — the host sshd, any loosely scoped management surface — is reachable from the container at mxview-host0's IP. To tighten further on a shared EMS IPC, either bind host services to specific non-mxview-host0 interfaces, or add an nftables rule on the host's input hook that drops traffic from the container's IP to host services. This is documented as-is rather than glossed over; the boundary is real but not absolute.
  • Self-signed internal TLS certs as shipped by the vendor. Not suitable for operator-facing TLS — deployments should put a proper reverse proxy (nginx, Traefik, Caddy running on the host) in front of 8443 and terminate TLS there with a real certificate.

Roadmap / open tasks

  • Wire MXview's SNMP trap listener to bind UDP 162 on the container's macvlan IP (in-container, using CAP_NET_BIND_SERVICE which the container already has).
  • Publish UDP 514 for syslog reception.
  • Persistent volumes for PostgreSQL data and MXview state so that container recreation doesn't wipe the database. Use named volumes, not bind mounts (named volumes remap correctly under --userns=auto).
  • A Quadlet .container unit for host-systemd-managed container lifecycle on the EMS IPC.
  • A systemd-networkd .netdev / .network drop-in to make the mxview-host0 sibling persistent across reboots without re-running host-network-setup.sh.
  • Automated upgrade testing: when MXview 1.7.2 ships, rebuild against the new zip and verify how many of the 11 documented vendor issues are fixed upstream.

Contributing

If you are trying to run this and something doesn't work, please:

  1. Check that the version you're building against is still 1.7.1. The MXview zip URL embeds a specific product version and build date; if Moxa has cut a new release, some of the documented bugs may be gone and some may have moved.
  2. Read the relevant VENDOR_ISSUES.md entry — it describes both the symptom and the actual root cause, not just the workaround, so you can tell whether your problem is the same or something new.
  3. If you hit a new vendor issue, open an issue on this repo with logs, strace output if possible, and we'll add it to VENDOR_ISSUES.md.

License

This repository contains only original work and references to publicly available Moxa materials. No Moxa source code, no Moxa binaries, no Moxa PDFs. The scripts and documentation here are released under the MIT license — see LICENSE, with a nominative-use note on Moxa trademarks in NOTICE.

You must obtain MXview One from Moxa under Moxa's own license terms; the 90-day trial is downloadable from their website, and the Containerfile fetches the .deb package directly from Moxa's public CDN at build time.

About

Moxa MXview One 1.7.1 containerization for Podman with vendor-bug workarounds

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors