Skip to the content
WellKept

// The agent

What the agent reads

Everything the agent reads, the four commands it may run as root, and what it can never do.

What it is#

wellkept-agent is a single static binary. Every minute it tells WellKept it is alive. Every five minutes it sends a snapshot of the server's posture. It never changes anything on the server, and it never runs anything WellKept sends it.

This page lists what it reads, what it may run as root, and what it can never do, so you can decide whether it belongs on a client's server.

What it reads#

Section Where it comes from
Host /etc/os-release, the kernel version, uptime, whether a reboot is waiting, the time zone and the host name
Resources Load, memory, and disk and inode use on each real filesystem
Updates apt-get -s dist-upgrade (a simulation) against the agent's own package lists, dpkg-query, and the unattended-upgrades settings
SSH sshd -T, or sshd_config and its includes when that is not allowed
Firewall The ufw configuration, nft list ruleset, and whether fail2ban is running
PHP The installed versions, their FPM services and pools
Services systemctl listings, package versions, and which addresses each service listens on
PM2 PM2's saved process list and pid files, and whether it starts at boot
Sites The nginx and Apache virtual hosts, then per document root the WordPress version file, Laravel's composer.lock or installed.json, package.json and the index file. For a WordPress site also the name and version at the top of each plugin and theme, read from the first 8 KiB of the plugin's main file and the theme's style.css

It never reads .env, wp-config.php, database credentials, private keys or logs. Nothing outside the files and commands above is touched. Run sudo -u wellkept wellkept-agent collect on a server to see exactly what would be sent. It prints the payload and sends nothing.

What it may run as root#

The agent runs as the unprivileged wellkept user. /etc/sudoers.d/wellkept lets it run four commands as root, with exact arguments and no wildcards:

wellkept ALL=(root) NOPASSWD: /usr/sbin/sshd -T
wellkept ALL=(root) NOPASSWD: /usr/sbin/nft list ruleset
wellkept ALL=(root) NOPASSWD: /usr/bin/ss -ltnpH
wellkept ALL=(root) NOPASSWD: /usr/local/bin/wellkept-agent probe-pm2
  • sshd -T prints the effective SSH configuration, which needs the root-only host keys. Without it the agent parses the configuration files.
  • nft list ruleset needs a network capability. Without it the firewall section reports the rules as unknown.
  • ss -ltnpH names the process behind each listening socket. Without it addresses are matched to services by their default ports.
  • probe-pm2 reads PM2's files under home directories that Ubuntu makes private. It only reads files. It never runs pm2, because pm2 jlist starts a PM2 daemon when none is running, and that would be an action on your server.

Every call is sudo -n, so a missing or edited sudoers file makes a probe fall back rather than hang. The agent refuses to start as root.

The package lists#

On Debian nothing refreshes apt's package lists unless unattended-upgrades is set up, so a count of pending security updates taken from the system's lists can be days or months old. The agent runs apt-get update once an hour into its own directory, /var/cache/wellkept/apt, as its own user. It writes nothing under /var/lib/apt and never takes the dpkg lock, so it cannot collide with unattended-upgrades or with you running apt. The lists take roughly 100 to 150 MB.

Set apt_refresh_interval = 0 in the configuration to use the system's lists instead, at the cost of accuracy.

The network#

  • Outbound HTTPS only, to the configured API, verified against the system's certificate authorities, TLS 1.2 or newer.
  • No listening socket, and no redirects followed.
  • Nothing the API returns is executed or interpreted as a command. A heartbeat answer carries two yes or no flags (re-check, rotate the token) and two check intervals, which the agent clamps to safe limits.

Files#

Path Owner and mode Holds
/usr/local/bin/wellkept-agent root, 0755 the binary
/etc/wellkept/agent.conf root, 0644 configuration
/var/lib/wellkept/token wellkept, 0600 in a 0700 directory this server's token
/var/cache/wellkept/apt/ wellkept, 0700 the agent's own package lists
/etc/sudoers.d/wellkept root, 0440 the four commands above
/etc/systemd/system/wellkept-agent.service root, 0644 the daemon

The token never appears in a log: at most its first characters do. The service is sandboxed by systemd (read-only system, private temporary files, a syscall filter, no writable memory that is also executable).

Configuration#

/etc/wellkept/agent.conf, one key = value per line, with intervals in seconds. Restart the service after editing.

api_url = https://app.wellkept.io/api/v1
heartbeat_interval = 60
snapshot_interval = 300
apt_refresh_interval = 3600
log_level = info

Tokens and updates#

  • Rotating the token. From the dashboard, the next heartbeat asks the agent to rotate. From the server, run sudo wellkept-agent rotate-token. Either way the previous token keeps working for ten minutes, so a crash halfway never strands a server.
  • Updates are off by default. The agent logs when a newer release exists and never replaces itself. The optional daily update timer installs a release only if it is newer, not below the minimum the API names, fetched over HTTPS, matches its SHA-256 checksum and carries an Ed25519 signature that verifies against the public key compiled into the running binary. Anything else is refused and the download deleted.

When something fails#

  • A probe that fails or runs out of time is listed as a probe error and its section is left out. The report still goes. WellKept skips the checks that read the missing section, and a failed probe never resolves a finding.
  • If the server is revoked or removed in the dashboard, the agent logs it and stops, and systemd leaves it stopped.
  • Network and server errors back off up to ten minutes. The daemon never exits for them. A hung agent is restarted by systemd's watchdog.

Limits#

  • Ubuntu and Debian only. Other distributions get a host section and probe errors.
  • PM2 restart counts are not reported, because only PM2's own daemon knows them.
  • A site behind a proxy-only virtual host is reported with its proxy target as its path, unless exactly one PM2 app is saved.
  • Let's Encrypt certificate files are readable only by root, so the agent usually cannot report their expiry. An SSL monitor covers that from outside.