Skip to content
Harry Fohmann
← Security Practice
Hardening · Configuration

CIS hardening, with reasons

The benchmark failed 106 checks on my hypervisor. I fixed twelve, rejected about fifty-five in writing, and the one real finding came from a rule I nearly dismissed.

As of September 2026

Starting point

Wazuh runs a configuration assessment against the CIS benchmark on every agent. On my Proxmox hypervisor it reported 81 checks passed, 106 failed and 20 not applicable: a score of 43 %. That number looks like a problem. The question was whether it is one.

The score is not the finding

The same assessment across twelve hosts in the lab landed between 37 % and 51 %. The hypervisor was not an outlier; it was average.

A low score is the normal state of an unhardened Debian system measured against a default policy. Chasing the number on one host would have left eleven others with the same picture. So I read the policy as a source of ideas, not as a specification.

Step 1: check the rules before the system

Before changing anything, I read the failing rules themselves. Five of them can never pass, whatever the system looks like. One demands that the root account is locked and has a password set. Another wants two contradicting password-hashing settings at the same time. One fails on the text output of a command even though the service it checks is demonstrably running.

A failed check is a claim about a rule, not about the system. The policy gives no reason and no actual value, so the host has to be asked separately.

Step 2: reject with a written reason

About 55 checks do not fit a hypervisor, or would actively harm it. Each was rejected with a reason in the documentation, for example:

  • Separate partitions with noexec cannot be retrofitted on a running system, and noexec on /tmp regularly breaks package and backup hooks.
  • An additional host firewall would duplicate the hypervisor’s own firewall and the segmentation at the gateway, and is the quickest way to lock yourself out.
  • Disabling SSH forwarding would break the jump-host path to a separate VM.
  • Locking root would remove the emergency access at the physical console.
  • File integrity monitoring and remote journal forwarding are already covered by Wazuh.

Step 3: quick wins, each verified

Nine checks were fixed with little risk: missing AppArmor utilities installed, an unneeded telnet client removed, cron files and directories restricted, sudo logging switched on. Every step ran as a dry run first; the sudo change was syntax-checked before it went live, because a broken sudoers file locks you out.

The check that mattered came afterwards. Restricting the cron directory is exactly the kind of change that can silently kill a cron job, and one of those jobs is the heartbeat that reports a complete outage of the host to an external service. I confirmed that the external service kept receiving its pings before calling the change done.

Step 4: decisions that need data

Two changes carried a real risk of locking me out, so I decided them on data rather than assumption:

  • Restricting SSH to named users. I queried thirty days of login events in the SIEM: 87 successful SSH logins, all by the same user from the same workstation. The restriction would lock out no one. I then tested a login from a second session and saw it arrive in Wazuh before closing the first.
  • Password quality. Minimum length 14, deliberately without character-class rules, which NIST SP 800-63B advises against, and not enforced for root, so emergency access at the console stays possible.

The one real finding

Only one failed check pointed to an actual weakness: an NFS server was running on the hypervisor. It exported a storage volume read-write, with no_root_squash, to an IP address that had belonged to a virtual machine I had decommissioned that same day.

That combination means root on the client becomes root on the hypervisor’s storage, and the only access control is the client’s IP address. That address was now free. Anyone who took it in that network segment would have had write access as root.

The export was documented nowhere, so it was on no decommissioning list. I checked that no client was connected, removed the export, disabled the NFS server and verified that all legitimate storage mounts were unaffected.

What no scanner found

The follow-up check after the quick wins turned up one more issue: a sudo configuration file with permissions of 640 instead of 440. sudo still read it, so nothing appeared broken. No CIS rule looks at that; the routine of running visudo -c after every change did.

What I deliberately did not do

The auditd block, about 26 checks, is the only one with a clear security benefit. I still left it out for now. Installing auditd changes where the kernel’s audit events go. Some 5,000 AppArmor events a day currently reach Wazuh through the system journal. With auditd, they would either vanish silently from the SIEM or arrive twice. I documented the decision, the measurements and a three-step path for introducing it later, each step reversible.

What I took away

  • A benchmark score is not a target. It is a list of ideas with a lot of noise.
  • A failed check is a claim about a rule. Ask the system before you change it.
  • Every rejection needs a written reason, or the next person assumes it was simply forgotten.
  • Decommissioning only removes what is documented. Anything undocumented stays behind.
  • Verify with an independent source, not with the shell you just changed things in.

What this means for a business

Compliance scores make attractive KPIs, and that is the risk. A team measured on the score fixes whatever raises it, not whatever reduces risk. It may even change settings that harm operations. The useful question for a board is not “what is our score?” but “which checks did we reject, and why?” A hardening project without documented exceptions has either not really been done or has broken something.

The real finding here came from decommissioning, not configuration. When systems are retired, every connection, share and permission they had needs to go with them. That only works if those connections are documented in the first place. Decommissioning is a security process, not a clean-up job.

← Back to Security Practice