Linux Agent Guide

Install Options

One-line installer (recommended)

From the dashboard, open Agent Setup and switch the OS toggle to Linux. Pick an agent token (the dropdown only offers active tokens with public download enabled and a target OS of Linux or Either) and your subdomain — the page renders the exact command to copy:

curl -fsSL https://etducky.com/install.sh \
  | sudo bash -s -- --token=<TOKEN_ID> --subdomain=<SUBDOMAIN>

The installer enrolls the host, writes /etc/etducky/agent/AgentConfig.json (owner etducky, mode 0600), and enables + starts the service. It is idempotent: re-running on the same host re-uses the existing agent record (keyed on /etc/machine-id), rotates the agent's bearer token, and restarts the service cleanly. Add --enable-smart or --disable-smart to answer the SMART disk-monitoring prompt non-interactively (opting in installs smartmontools if missing).

Package downloads

The Agent Setup Linux tab also links the raw packages (or fetch them from Downloads):

  • .deb — Debian / Ubuntu: sudo apt install ./etducky-agent_<version>_amd64.deb
  • .rpm — RHEL / Fedora / Rocky: sudo dnf install ./etducky-agent-<version>-1.x86_64.rpm
  • .run — distro-agnostic self-extracting installer for hosts without dpkg/rpm: sudo bash ./etducky-agent-<version>.run

A SHA256SUMS file is published alongside the packages for verification. Package installs still need enrollment — use the one-liner on fresh hosts, or run the packaged install script with a token afterwards.

The systemd Service

The agent runs as the etducky-agent systemd unit under the unprivileged etducky user — not root. Capabilities are scoped to exactly what eBPF and diagnostics need (CAP_BPF, CAP_PERFMON, CAP_NET_ADMIN, CAP_SYS_PTRACE, CAP_DAC_READ_SEARCH), with filesystem hardening (ProtectSystem=strict, read-only /home, private /tmp) and resource caps (512 MB memory, 50% of one CPU).

sudo systemctl status etducky-agent
sudo journalctl -u etducky-agent -n 50 --no-pager

Paths

  • /opt/etducky/agent — binary and eBPF objects (read-only)
  • /etc/etducky/agent/AgentConfig.json — configuration (mode 0600; the agent refuses to start if permissions are weaker)
  • /var/lib/etducky — local state
  • /var/log/etducky — log files (stdout/stderr also go to the journal)

To collect a support bundle (agent 2.8.5.2 and later), run sudo -u etducky etducky-agent --diag; see Support Bundle.

Online/offline status is tracked from heartbeat freshness in the cloud, not by systemd. Restart=on-failure handles real crashes.

The Sudoers Policy (/etc/sudoers.d/etducky-rmm)

Packages ship a deliberately narrow sudoers drop-in. It grants the etducky user passwordless sudo for only these categories:

  • Read-only diagnostics — smartctl, dmidecode, lshw, lspci, lsusb, journalctl, dmesg. Root-required to read, but cannot mutate anything; used by heartbeat collectors and AI introspection.
  • Self-management — systemctl restart etducky-agent only (the agent's own restart after an in-place upgrade). Argument-restricted to the etducky-agent unit and the restart verb.
  • Package operations via the etducky-pm helper unit — all privileged package work (third-party app install/remove/upgrade and the agent's own self-update) routes through a constrained systemd template unit rather than a direct sudo apt/dnf. The grant is starting that unit and nothing else: systemctl start --wait etducky-pm@<encoded-op>.service. The agent picks the opaque instance id, but the actual operation is read from a request file and validated by the helper script against a fixed allowlist — only an apt/dnf/zypper/snap/flatpak paired with install/remove/upgrade of a syntactically-valid package name, run as fixed argv, is ever executed. So this constrained package install/remove/upgrade is passwordless by design, but the agent can never turn the grant into an arbitrary root command. Delete this rule to hard-disable all Linux package operations (patch, app-deploy, and self-update) and update out of band. See Package operations & the etducky-pm helper below.
  • Repository subscription via the etducky-subscribe unit — starting one fixed, argument-less unit that registers the host with the ET Ducky package repository (see Agent auto-update below). Unlike the etducky-pm template there is no instance id, no request file and no argument of any kind, so there is nothing about it the agent can influence; the repository URL is compiled into the script rather than passed in, for exactly that reason. It is a unit rather than a plain sudo call because the agent runs under ProtectSystem=strict and a sudo child inherits that read-only view of /usr and /etc. Delete this rule to subscribe hosts by hand instead; the agent then logs one line saying it is unsubscribed and carries on.
  • Network isolation — applying/removing the dedicated etducky_isolation nftables table (iptables fallback on hosts without nft) so a high-confidence ransomware detection can quarantine the host without waiting for an operator.
  • On-device approval prompt — launching zenity/kdialog/xmessage as the logged-in console user, never as root, for the unisolate approval flow below.
  • Run as root: starting the etducky-root-shell.sh script with one fixed command line, for Live Session shell commands sent with Run as root. The script runs nothing unless the host administrator created /etc/etducky/root-shell.enabled. See Running commands as root below.

Nothing else is passwordless. Direct sudo apt/dnf/zypper, firewall changes, user management, mounting and arbitrary systemctl run as root only through Run as root, on hosts that enabled it. Package installs, removals and upgrades are passwordless only through the allowlisted helper unit described above, never through a direct package-manager invocation.

Package operations & the etducky-pm helper

Every privileged package operation on Linux — deploying a third-party app, patching it, removing it, and the agent's own self-update — runs through the same constrained path, not a direct sudo apt/dnf.

Why the indirection. The etducky-agent service runs ProtectSystem=strict, so a package manager the agent spawns (even under sudo) inherits that read-only mount namespace and fails to write /usr or the package database. Instead, the agent writes a small request file (manager/op/package) to /var/lib/etducky/pm/<id>.req and asks systemd to start a separate, unsandboxed oneshot unit:

sudo -n systemctl start --wait etducky-pm@<id>.service

That unit (PID 1–launched, so it gets a fresh mount namespace where the package manager can work) runs the helper script /opt/etducky/agent/scripts/etducky-pm-op.sh. The helper validates the request against a fixed allowlist before it runs anything:

  • Manager must be one of apt, dnf, zypper, snap, flatpak.
  • Operation must be one of install, remove, upgrade.
  • Package name must match a strict pattern (no spaces, no shell metacharacters, no leading dash), and is passed as a single argv element — never through a shell.

Each (manager, operation) pair maps to a fixed command (for example apt:upgrade → apt-get install --only-upgrade -y <pkg>). The unit blocks with --wait and the helper writes the package manager's exit code and combined output back for the agent to read. Because the operation is read from the request file and allowlist-validated — not taken from the sudoers command line — the agent's single privileged capability (starting one unit) can never become an arbitrary root command.

The helper is dormant until both the agent's PatchExecuteEnabled config and the server's Patch Management execute flag are on; otherwise the agent simply never starts the unit.

Running commands as root

The Live Session shell runs commands as the agent’s etducky account. A command that needs root, such as a write under /proc/sys or /sys, modprobe, auditctl or journalctl for a system unit, is sent with Run as root ticked in the shell. All three of these must be true:

  1. The member holds the Linux root shell privilege (see Team). Workspace and organization owners hold it. Administrators and members hold it only when an owner grants it to them by name, and it cannot be attached to an invitation.
  2. The host administrator enabled Run as root on that host, then restarted the agent so it reports the setting:
    sudo install -o root -g root -m 0644 /dev/null /etc/etducky/root-shell.enabled
    sudo systemctl restart etducky-agent
    To turn it off, delete the file. The host refuses root commands from then on; after the next agent restart the dashboard also stops sending them.
  3. The host runs a Linux agent newer than 2.8.5.3, installed from the .deb or .rpm package, which ships the sudoers rule and the script. Hosts installed with the .run installer have no sudoers file, so Run as root is not available there. To check how a host was installed, run dpkg -s etducky-agent (Debian and Ubuntu) or rpm -q etducky-agent (RHEL, Fedora and SUSE); if neither finds the package, reinstall the agent from the package to use Run as root.

When one of these is missing, the shell refuses the command before anything is sent to the host, says which condition failed, and links back to this section. Without the privilege the refusal names it; without the host file or the right agent package it gives the two commands above.

How a root command runs. The agent starts one fixed command line, which is the only form the sudoers rule allows:

sudo -n /usr/bin/systemd-run --pipe --wait --collect --quiet /opt/etducky/agent/scripts/etducky-root-shell.sh

systemd starts the script as its own service, outside the agent’s sandbox, so the command has the same root access as a console session. The command reaches the script on standard input; the rule takes no arguments. The script refuses to run anything unless /etc/etducky/root-shell.enabled exists, is owned by root and is not writable by a group or other users. The agent cannot create that file.

Audit. Each root command is recorded in three places: the dashboard audit log entry shell_root_command, with the member, the source address and the command’s SHA-256; a line in the agent’s audit log with the command id; and the host journal (tag etducky-root-shell, facility authpriv), with the command id and SHA-256 before the command runs and its exit code after it. To read the host entries: sudo journalctl -t etducky-root-shell.

Limits. As in the rest of the shell, there is no interactive terminal, and the command’s input is empty. The timeout is 5 minutes; the script stops a command that passes it. Root commands go through the same blocked-command checks as other shell commands. Only typed commands run as root: saved scripts and commands the AI proposes never do.

sudo in the shell. On Linux agents newer than 2.8.5.3, typing sudo does not ask for a password. sudo fails at once unless one of the rules above covers the command, and the shell suggests Run as root. On Linux agents 2.8.5.3 and older, a sudo line in a Live Session still waits up to five minutes for a password prompt the dashboard does not show, and two such lines hold both of the agent’s shell slots; upgrade the agent.

Unisolate approval

Lifting network isolation is a dual-party action: in addition to the dashboard operator, a GUI confirmation prompt is shown to the logged-in console user on the host (via zenity/kdialog/xmessage). On headless hosts with no graphical session, the on-device prompt cannot be shown and the lift is rejected through that path.

eBPF Capture vs. Windows ETW

Kernel telemetry on Linux comes from eBPF tracepoint programs (libbpf + CO-RE), loaded when KernelEventsEnabled is true in AgentConfig.json (the default). Three provider buckets can be toggled via KernelProviders:

  • Process — exec and exit tracepoints
  • File — open and close syscalls
  • Network — connect and accept (IPv4 + IPv6)

Events are normalized to the same shape as Windows ETW events, so live-session AI analysis, inflection-bracketed correlation, and behavioral detections work identically on both platforms.

Kernel requirements

  • Kernel 5.8+ for CAP_BPF (older kernels fall back to broader capabilities).
  • Built-in BTF (/sys/kernel/btf/vmlinux) — standard on Ubuntu 22.04+ and Fedora 36+. For RHEL 8 / Ubuntu 18.04 / Amazon Linux 2, drop a matching <uname -r>.btf file into /opt/etducky/agent/bpf/btfhub/.
  • If the eBPF library is absent or capture is disabled, the agent runs in a no-op capture mode: heartbeats, health metrics, live sessions, and remote desktop all keep working — only kernel events are off.

Kill switch: set "KernelEventsEnabled": false in /etc/etducky/agent/AgentConfig.json to disable kernel capture without uninstalling.

Deploying third-party apps

You can deploy third-party applications to Linux agents through the host's OS package manager. On a Software Catalog entry, set the Linux package id field (the package name the target distro's manager knows, e.g. htop) — that gives the entry the linux_pm deploy method. When you deploy, each Linux agent installs the package through the same allowlisted etducky-pm helper unit described above (apt/dnf/zypper/snap/flatpak install), so no direct root shell is involved.

See Patch Management for the full deploy workflow (choosing a method, scheduling, and reading back per-agent job results).

Agent auto-update

The Linux agent can keep itself current by upgrading its own package through the OS package manager — routed through the same etducky-pm helper unit, so it works under the sandboxed service.

It periodically fetches the published version manifest (version.json) and compares the advertised Linux version against the running one. Self-update is gated on the agentLinux.autoUpdate flag in that manifest: when it's off, the agent never self-upgrades and you update out of band. When it's on and a newer version is published, the agent requests an upgrade of the etducky-agent package via the helper; the package's post-install step restarts the service.

Auto-update needs the host to be subscribed to the ET Ducky package repository and the etducky-pm sudoers grant present. Without the repo the upgrade finds no candidate; without the grant the attempt fails fast with a clear log line.

Repository subscription

Upgrading through a package manager means upgrading from a repository. A downloadable .deb is not enough: apt resolves candidates only from configured repositories, so a host with no ET Ducky sources entry reports "already the newest version" no matter what is published.

Hosts are subscribed in one of two ways:

  • At install. The one-line installer fetches the signing keyring to /usr/share/keyrings/etducky-archive-keyring.gpg and writes /etc/apt/sources.list.d/etducky.list (or /etc/yum.repos.d/etducky.repo), pinned to that keyring. Pass --no-repo to skip it on air-gapped hosts.
  • Afterwards, by the agent itself. Hosts enrolled before the repository existed have no sources entry — and could not get one by updating, because updating is the thing that needs it. On its first update check after upgrading to a build that supports this, an unsubscribed agent registers itself once. If the repository is unreachable it changes nothing and retries on the next cycle.

The subscribe step never leaves a sources entry it has not verified. It installs the keyring, writes the entry, refreshes only that source, and confirms a candidate resolves — if any of that fails it removes what it wrote and reports the host unchanged. A broken sources file would break apt-get update for every package on the host, not just ours, so failing unsubscribed is always the safer outcome.

Network access

If you allowlist outbound traffic, auto-update needs these in addition to the API endpoint:

  • https://etducky.com/etducky-archive-keyring.gpg — the signing keyring, fetched once at subscription
  • https://etducky.com/apt/ — the signed APT index and package pool (Debian/Ubuntu)
  • https://etducky.com/yum/ — the signed YUM repository (RHEL/Fedora/SUSE)
  • https://etducky.com/downloads/version.json — the version manifest the agent compares against

Blocking these does not raise an error anywhere in the console. The agent keeps reporting healthy on the version it has, and the only symptom is a fleet that quietly never updates. If you allowlist by URL rather than by host, check these first when agents are not moving to a version you have published.

Self-hosted (local‑first) deployments do not use this path. Their agents point at your own server, which does not serve a signed package repository, so repository subscription is skipped and Linux agents are updated out of band.

File transfers & POSIX paths

File transfers to Linux agents accept POSIX absolute paths — a single leading forward slash, e.g. /etc/etducky/… or /opt/… — so pushes and pulls aren't rejected as “not rooted” the way a Windows drive path would be validated. The same anti-traversal guards apply on both platforms: ./.. segments, empty interior segments, and control characters are rejected, and the agent re-checks the resolved path at open time.

Known Platform Differences

The Linux agent is at operational parity with Windows for enrollment, heartbeats, health and hardware metrics, live sessions, remote desktop, inventory, alerts, and behavioral rules. Remaining differences:

AreaWindowsLinux
Kernel telemetry breadth30+ configurable ETW providers (registry, DNS, .NET, firewall, …)3 eBPF buckets: Process, File, Network
Remote desktop captureDXGI Desktop Duplicationx11vnc (Xorg) or desktop portal / PipeWire (Wayland), with an H.264 GStreamer helper
Privileged commandsAgent runs as LocalSystem; commands execute directlyRun as root per command, for members with the Linux root shell privilege on hosts that enabled it, with an audit entry per command
SMART disk healthOn by default (WMI)Opt-in at install (--enable-smart); requires smartmontools
Distro / kernel / init reportingFull OS version reportingPartial — basic OS info reported; richer distro detail still in progress
H.264 remote desktop prerequisitesMedia Foundation (built in)Needs gstreamer1.0-vaapi (hardware) or gstreamer1.0-plugins-ugly (x264 software); falls back to JPEG without them

Troubleshooting

  • Service won't start; journal says "config has weak permissions" — AgentConfig.json must be 0600. Re-run the one-liner; it sets permissions atomically.
  • Crash loop on start — check journalctl -u etducky-agent -n 200. Common causes: kernel older than 5.8 with eBPF enabled, or missing libssl3.
  • 403 from the one-liner — the token isn't public-download enabled, or its target OS is Windows-only. Fix it on the Agent Setup page.
  • Agent online but no kernel events — the host is likely in no-op capture mode (no BTF, old kernel, or KernelEventsEnabled false). See the eBPF section above.