Skip to content

Posture

See which ports each host is listening on, whether the firewall actually covers them, and get told when a database or a Docker-published port becomes reachable from the internet.

Posture answers a question access control cannot: what is this host exposing right now? A small collector on each Linux host reports its listening ports, the process or container behind each one, and its firewall rules. Shellius works out which of those ports are reachable, and raises a finding when something is exposed that probably should not be.

It is an exposure inventory, not monitoring and not a vulnerability scanner. It does not probe your hosts from outside and it does not look for CVEs.

Scope

Linux hosts only. Windows and RDP-only servers are not collected, and are labelled as such rather than shown as clean. Firewall verdicts are computed for ufw and firewalld. On a host using raw nftables or iptables, ports are reported with a firewall state of unknown instead of a guess.

Required permissions ​

PermissionLets you
posture.readSee the Posture and Services & ports pages, and the posture tabs on a server.
posture.muteMute, unmute and acknowledge findings.
posture.expected_portsDeclare a port as expected on one specific server.
posture.exportDownload findings and port inventories as CSV, JSON or PDF.
posture.settingsSet retention, the organisation-wide expected-public list, and alert rules.

Export is separate from viewing on purpose: an export takes security data out of Shellius, where access control no longer applies to it.

Install the collector ​

The collector is installed as part of a normal bootstrap, so newly onboarded hosts report without any extra step. It runs from its own systemd service and timer, about every five minutes, entirely separate from check-principals. A collector that crashes cannot affect SSH logins.

For hosts that were bootstrapped before posture existed, or that use a stored identity and were never bootstrapped at all:

  1. Open Servers and select the hosts.
  2. Choose Install collectors.
  3. Pick Posture collector only. This adds the collector and its timer and does not touch sshd, CA trust or check-principals, so it is safe on hosts that already work.
  4. Review the plan, supply credentials for any host without a saved identity, and run it.

To see which hosts are reporting, open Posture and select Coverage. Each server is Reporting, Stale (it has stopped reporting) or Not installed.

How a verdict is reached ​

The collector sends raw, observable facts only. Shellius recomputes reachability and findings itself, and ignores any verdict a host sends about itself. A compromised host that could report "all clean" would defeat the point.

Each listening port gets a reachability:

ReachabilityMeaning
InternetBound to a public or wildcard address, and the firewall allows it or has no default deny.
FirewalledBound publicly, but the firewall denies it.
LANBound to a private address.
LoopbackBound to 127.0.0.1 or ::1.
ContainerListening only inside a container.
UnknownThe firewall engine is one Shellius cannot reason about.

Findings ​

FindingSeverityRaised when
DOCKER_FIREWALL_BYPASSCriticalDocker publishes a port that the host firewall would have blocked. Docker's NAT rules are applied before the firewall's input rules, so the port is open regardless of ufw or firewalld.
SENSITIVE_PORT_EXPOSEDCriticalA database, cache, cluster or management port — PostgreSQL, MySQL, Redis, MongoDB, Elasticsearch, the Docker API, etcd, the Kubernetes API, RDP, VNC, SMB and similar — is reachable from the internet.
PORT_EXPOSEDHighAny other port is reachable from the internet and nobody has declared it expected.
NO_HOST_FIREWALLHighThe host has no firewall.
FIREWALL_INACTIVEHighA firewall is installed but switched off.
SENSITIVE_PORT_WILDCARD_BINDMediumA sensitive port is bound to every address and only the firewall keeps it private.
STOPPED_SERVICE_PORT_OPENMediumThe firewall allows a port whose service is installed but stopped.
SENSITIVE_PORT_LANLowA sensitive port is reachable from the private network.
STALE_FIREWALL_RULELowThe firewall allows a port that nothing listens on.
UNATTRIBUTED_LISTENERLowA port is open and its owning process could not be identified.
EXPECTED_PUBLICInfoAn internet-reachable port that is declared expected.
FIREWALL_STATE_UNKNOWNInfoReachability could not be determined.

Ports 22, 80 and 443 are treated as expected-public out of the box.

The Docker finding is the one to act on

ufw status will tell you the port is blocked, and it will be wrong. If you see DOCKER_FIREWALL_BYPASS, publish the container port on 127.0.0.1 instead of every address, or put it behind a reverse proxy.

Work through findings ​

Posture lists findings across every server you can see, filterable by severity, customer, environment and status. Services & ports shows the other view of the same data: every listening service across the fleet, from each host's latest snapshot. On a single server, the Open findings and Ports & services tabs show the same for that host.

A finding is in one of these states:

StatusMeaning
OpenNot yet acknowledged, muted or declared expected.
AcknowledgedSeen and accepted. Escalation stops, but the finding stays open.
MutedOut of sight until the mute expires. A muted finding notifies nobody.
Marked as expectedSomeone declared the port public on purpose. Kept so the inventory is complete.
ResolvedNo longer reported by any collector. Kept for history.

Findings resolve themselves. Close the port or fix the firewall, and the finding resolves on the next report.

Mute a finding ​

  1. Select Mute… on the finding.
  2. Choose how long under Mute for: 1 day, 7 days, 30 days, or Until date.
  3. Give a reason. It is required — a suppression nobody can explain later is worse than the noise it removed.

Mark a port as expected ​

If a port is meant to be public, say so instead of muting it forever.

  • On one server: select Mark expected on the finding and add a note. The note is required. Needs posture.expected_ports.
  • Across the organisation: add the port to the expected-public list under Administration › Exposure posture. Needs posture.settings.

Either way the finding drops to EXPECTED_PUBLIC at Info severity, shows which rule matched, and does not reopen.

Alert rules ​

Findings notify people through rules you define under Administration › Exposure posture, not through a fixed list of admins. A rule matches on any of severity, finding code, customer and environment, and routes to roles, a group or named users, in-app or by email. A rule can send immediately or as a daily digest, throttle repeat notices for the same finding, and escalate to another group if a finding is still open after a set number of hours.

A new organisation starts with one rule, Critical exposure, which sends critical findings to admins and super admins in-app and by email. You can edit or delete it.

What keeps this quiet:

  • Changes notify, scans do not. A finding that persists across three hundred reports is one notification.
  • Muted findings notify nobody, escalations included.
  • Acknowledging stops the escalation clock without resolving the finding.
  • Customer scope applies. A user limited to selected customers is never alerted about a customer they cannot see.

Email delivery needs a working mail setup — see Email.

Retention ​

Set under Administration › Exposure posture.

DataDefault
Snapshots7 days
Resource metric samples24 hours
Resolved findings90 days after they resolve

Export ​

Select Export on Posture or Services & ports, or select servers on the Servers page for a bulk export. Choose CSV, JSON or PDF, pick the columns, and for several servers choose between One file and ZIP, one file per server.

Troubleshooting ​

A bootstrapped server shows no posture data. Give it a few minutes after install. If it is still empty, check the timer on the host:

bash
systemctl status shellius-posture.timer
journalctl -u shellius-posture.service -n 50

The host needs outbound HTTPS to the Shellius API, the same as check-principals. See Ports and firewalls.

A container shows as runtime:<id> instead of its name. The collector deliberately has no access to the Docker socket, so it cannot always resolve a container's name. The port and the finding are still correct.

Every port on a host says unknown. The host uses a firewall engine other than ufw or firewalld. Shellius withholds a verdict rather than guessing.

Next steps ​

Was this page helpful?No, tell us why