OS hardening: where configuration diverges from policy
Servers are rarely deployed from a single image. Some machines are built from a reference template, some from images of previous releases, and individual nodes have been manually patched for a specific task.
On top of that, changes accumulate over years: password complexity requirements were disabled so they wouldn't interfere with automated deployment, filtering rules were relaxed for an integration, and permissions were expanded instead of configuring a service account.
That's how configuration drift appears. The baseline security policy is documented in a regulation, but the actual state of a host does not match it — and there is no way to verify this without a full pass across the machines.
Uncontrolled privileges work the same way: permissions are granted manually for a one-off task, almost never revoked afterward, and neither the administrator nor the support service has a precise answer to who currently holds them.
Server security configuration in such a mode rests on the memory of a specific engineer. The very first external check or audit reveals dozens of deviations on each machine: extra network services, accounts without expiration dates, daemons running with excessive privileges.
The investigation stretches for weeks, because it is not enough to close a deviation — you need to understand what dictated it and what will break in production after the change.
OS hardening is not a one-time action but a mode in which the state is verifiable at any moment.
We'll break down how this is addressed: a single standard for all machines, declarative application of settings through automation roles, and regular reconciliation of fact with policy.
What a unified server security profile provides
A unified security profile is a reference set of settings: authentication rules and password lifetimes, directory permissions, composition of running services, logging policies, and network port filtering.
We take CIS requirements as the basis — levels 1 and 2, from which we explicitly strike out what is inapplicable to the node's role. Without this step, the profile breaks working applications, and it gets disabled in the very first week.
Server standardization removes the manual checklist from node commissioning. A set of Ansible roles and playbooks is applied to the fleet in a single run, each server's state is compared against the reference, and discrepancies are output as a list.
Server commissioning time is reduced from several days of approvals and manual configuration to tens of minutes, and onboarding a new engineer comes down to one question: has the playbook been applied or not.
Hence an eliminated class of failures, not a single incident. As long as nodes are configured identically, a failure is reproduced on a test bench and fixed by a change in one place in the profile, rather than by searching for differences between "similar" servers.
Extra services, guest accounts, and outdated encryption algorithms disappear from the reference — typical causes of failures during checks simply do not pass rollout.
Readiness for an audit stops being an emergency. The deviation report is captured by the same playbook in check mode, without changes on the servers: the audit receives a list of facts, not promises.
The share of nodes that have deviated from the reference and server commissioning time become working metrics — they show whether the fleet remains in the reference state between checks.
Ways to bring the OS to the standard: comparison of approaches
The list of standard requirements is the same for everyone, but the ways to achieve it differ radically — both in labor costs and in how provable the result is to an auditor at all.
Manual server configuration against a checklist closes the items formally, but the node's state lives its own life: someone adjusted a parameter, a package was updated — and compliance is lost without any signal.
Four options in practice
| Option | When it fits | Limitations |
|---|---|---|
| Manual configuration against the requirements list | Single nodes, test benches, one-off experiments | Configuration drift, the result is not reproducible, audit rests on the executor's memory |
| Reference system image | Identical servers are deployed from scratch, nodes are replaceable | A change requires rebuilding and redeploying; the image does not affect an already running fleet |
| Declarative state description with roles (Ansible) | A fleet of dozens of nodes, repeatability and rollback are needed | Requires discipline: inventory, idempotent tasks, role versioning |
| Compliance scanners without change automation | Reporting and deviation control are needed | They show discrepancies but do not eliminate them; the report captures a snapshot, not the state |
In practice, the strongest combination is: declarative roles bring a node to the reference, the scanner confirms the result and remains a regular drift check.
The reference image complements it for new machines, and manual configuration remains only where automation is more expensive than the node itself.
Implementation stages: from audit to handover of regulations
The CIS standard is not a single set of settings but several levels of strictness, and it cannot be applied blindly: some requirements affect well-established integrations and non-standard applications.
Therefore, we carry out the work sequentially, recording the state of the environment before and after each step.
- Server inventory: collect node roles, purpose, network segments, owners; note where the policy applies in full and where exceptions are needed.
- Profile scheme: divide the fleet into classes — base node, web server, DBMS, domain controller — and assign each its own strictness level.
- Preparation of descriptions: write idempotent Ansible roles with tasks split by tags, so both the full profile and an individual rule can be run.
- Testing on a bench: deploy a copy of the node, run the profile, see what breaks — logging, mounting, service accounts.
- Enablement on the production environment: in groups of several nodes, first in check mode without changes; keep rollback ready and execute it with a single command.
- Acceptance: compare the report for each rule with the target level, record justified exceptions, verify that the state persists after reboot.
- Documentation handover: change regulations, description of roles and profiles, procedure for connecting a new node, and the person responsible for policy review.
The output is not a one-time configuration but a repeatable process: a new server is brought to the standard by the same set of roles, and deviations are visible in the report rather than surfacing during the first check.
Output artifacts: configurations, diagrams, regulations
After project delivery, your team is left not with one-off server changes but with a reproducible reference: configuration descriptions by which a new machine is brought to the same state with a single command.
Below is the list of artifacts we hand over with the work, each in a form suitable for edits without our involvement.
- Hardening profiles. Configuration descriptions for Linux and Windows, arranged by roles: kernel and network stack parameters, service composition, password and lockout policies, logging rules. Each item is tied to CIS and marked with which inherited applications are affected by it.
- Access policies. Login by keys and certificates, prohibition of direct login under service accounts, restriction of privilege escalation, session lifetimes.
- Node interaction diagrams. A map: what communicates with what, over which ports, where the control points and trust relationships are, which channels are encrypted.
- Role separation matrix. Who can do what — read configurations, apply profiles, edit exceptions. The right to change the reference is separated from the right to apply it.
- Change management regulation. Request, justification, testing on a bench, application, rollback. It also includes the procedure for reviewing the reference: frequency and responsible person.
- Reports and exceptions. Check results before and after, a list of deliberately disabled rules with justification and the conditions under which they are enabled.
- Rollback procedure. What to do if, after applying the profile, a node becomes inaccessible or a service does not start.
Case study: reducing deviations from the reference sixfold
The customer's fleet is about three hundred nodes: several Linux families and different Windows branches, some machines in the cloud, some on their own hardware.
Settings were changed manually as tasks progressed, there was no unified reference profile, and a pre-release audit showed an average of 47 deviations per node — from service permissions and extra accounts to ports exposed externally.
Commissioning a new machine into production took two days and still allowed discrepancies with the settings of neighboring nodes.
How we brought it to the reference
We assembled the reference as Ansible roles: common requirements were put into variables, and differences between OS families into separate task sets, so that one compliance profile covers the entire mixed fleet.
Each rule is idempotent and is first run in check mode: you can see exactly what will change on a specific node before applying it.
Exceptions are not silenced but live in a separate list with justification, owner, and review deadline — otherwise the reference quickly diverges from reality.
Metrics before and after
After three remediation cycles, deviations per node dropped to 8, and the share of machines passing the check on the first attempt grew from 31% to 94%.
Building a new node was reduced from two days to 35 minutes: it comes up from the template and is immediately brought to the reference, and manual edits after installation are gone.
Before release, a separate preparation phase is no longer needed — the check runs with the same set of rules as the configuration, and discrepancies automatically go into the report.
Rollback remains manageable: the configuration state is snapshotted before changes, so returning to the previous version takes minutes, not hours of investigation. Now let's look at how the same approach transfers to a fleet with non-standard and outdated hardware.
How OS hardening for Windows differs from Linux
The difference starts with the access rights model. In Windows, rights are tied to an account identifier: access to an object is described by an access control list, privileges are granted to groups, and inheritance is defined by the domain hierarchy.
In Linux, the same restriction is assembled from three layers — owner and group permissions on a file, separate privileges for running as superuser, and mandatory access control, which narrows a process's capabilities even after successful login.
Services and logging are structured differently. In Windows, background processes are gathered in a single service manager, each has its own account, and events go to a common log with numeric codes and audit categories.
In Linux, these are independent daemons and units writing to the system log or to the kernel audit subsystem. The meaning of events is the same, but thresholds and correlation rules are built separately for the platforms.
Application of settings also differs. Windows pulls configuration from Group Policies, which are inherited by levels: you need to check the effective policy, not just the configured value.
Linux reads parameters from configuration files, and here the declarative approach works — the role describes the desired end state, and a repeated run breaks nothing.
The reference remains common, however.
Requirements for password policy, lockout after a series of failed logins, auditing of logins and permission changes, the minimum set of services, and privilege separation are formulated identically — only the implementation and verification method differ.
We maintain one profile with two strictness levels, and for each system we write its own implementation for bringing it to the reference. A regular run produces a discrepancy report: what and on which node has drifted from the reference.
Thus a heterogeneous fleet — Linux and Windows in one infrastructure — is managed by a single regulation, not by two unrelated sets of instructions.
Common technical doubts before starting
Doubts before starting are usually not about the tool, but about how it will coexist with a live infrastructure where some servers have been running for years and touching them is scary. We have collected four questions that are asked at almost every review, with substantive answers.
Will implementation require stopping services?
We go from measurement to changes. First, inventory and state check in read-only mode — configurations are not changed, services are not restarted. Then changes are applied in groups with limited concurrency, and reboot is performed in an agreed window and one node at a time.
Checks produce false positives — what should be done about them?
Some reference requirements are inapplicable: for example, banning kernel modules for a service that does not work without them. Such items are not silently disabled — we formalize an exception with justification, role, and node. The exception lives in the same configuration description and is not lost on the next run.
Do inherited settings conflict with the standard?
We describe them as explicit deviations with a reason and owner, rather than rewriting them blindly. The priority is application operability: the reference is first applied on a test environment, discrepancies are analyzed, and only then does the rule go to production.
Who maintains the reference after OS updates?
The reference is stored as code in a repository with change history and pipeline validation — an update is a change to one file, not a manual pass across servers.
Compliance checking runs on a schedule and shows discrepancies per node; rules for new OS versions are added without stopping operations.
Let's discuss the task: what data is needed for assessment
The assessment of bringing a fleet to a CIS profile depends not on the number of servers but on the heterogeneity of the environment.
Until it is clear how many different distribution branches there are, what roles the nodes carry, and what is already described in existing roles, any timeline figure will be a guess.
Therefore, before assessment we collect initial data about the environment — from it grows a work plan broken down by groups.
- Node inventory: Linux families and branches, Windows editions, the number of servers and templates from which they are deployed.
- Roles and purpose: web layer, DBMS, brokers, domain controllers — rule sets and permissible exceptions depend on this.
- Automation state: whether there is an existing inventory and roles, and what populates the node list — a static file or a query to the configuration management system.
- Check profile: baseline or enhanced CIS level, applicable sections, rules that cannot be enabled because of outdated applications.
- Access: a service account with privilege escalation, the method of connecting to the fleet, a jump host, and key rotation procedure.
- Maintenance windows and rollback: whether configuration can be changed in groups, where service restarts require approval, and whether snapshots are available before a run.
- Result verification: which scanner we use to confirm compliance and in what form the audit report is needed.
Then we discuss the task substantively: we reconcile discrepancies in the data, clarify disputed rules, and come back with a timeline assessment and work plan — first a pilot group, then the rest of the fleet.







