On this page
A server admin opens a firewall port at 2:00 AM to fix an outage. The incident closes. The port stays open. Nobody notices until a security scan flags it three months later, or until someone else finds it first.
This is the pattern behind most infrastructure as code (IaC) security failures. Not a sophisticated attack. A gap between the infrastructure that actually exists and the infrastructure that’s written down as code.
For years, securing a server meant hardening the box itself: patching the operating system, locking down SSH, configuring the firewall, watch the logs. Tools like CyberPanel handle a large share of that work directly, and it’s still necessary. But as more provisioning moves into Terraform, OpenTofu, and Ansible, a second layer of risk has opened up above the server: the pipeline that creates it.
That shift matters most for anyone managing more than a handful of machines. A hosting provider spinning up VPS environments for dozens of customers, or a small team running production and staging across three cloud accounts, is no longer securing one server at a time. A single bad Terraform module, applied once, can reproduce the same misconfiguration across every environment that module touches. Manual server hardening doesn’t scale to that pattern. The fix has to live in the pipeline itself.
Why the pipeline needs its own security model
Infrastructure as code turned server changes into pull requests. An engineer edits a Terraform file, opens a merge request, and a continuous integration (CI) job applies the change. That’s a real improvement over manually clicking through a cloud console: changes are versioned, reviewable, and repeatable.
The problem is that most teams review that pull request the way they’d review an application code change, and infrastructure changes aren’t the same category of risk. A typo in a Terraform module can open a security group to the entire internet, grant an identity and access management (IAM) role far more access than it needs, or delete a production database. A standard code review often misses this, because the reviewer is reading configuration syntax, not scanning for security group ranges.
This is why IaC security has become its own discipline. It doesn’t replace server hardening. It sits above it, controlling what actually gets deployed in the first place.
Policy as code: rules that run before the damage does
Static scanning tools like Checkov or tfsec catch a lot of common mistakes: unencrypted storage, public buckets, missing tags. They’re useful, and most teams should run them. But a scanner that flags a problem in a pull request comment still depends on a person reading the comment and deciding what to do with it.
Policy as code moves the check earlier and makes it non-optional. Instead of flagging a problem after the fact, a policy engine evaluates the Terraform plan before it applies and blocks anything that violates a rule. A common first rule looks like this: block any plan that attaches a wildcard 0.0.0.0/0 ingress rule to a database security group. Write that rule once, and it can’t be forgotten during a rushed deployment the way it might be during a late-night console fix.
Open Policy Agent has become the standard engine for this kind of check, using a purpose-built language called Rego to write a rule once and apply it everywhere: every stack, every team, every environment. Platforms built around this idea, like Spacelift, embed that kind of check directly into the plan and apply workflow. It doesn’t ask permission to stop a bad plan. It stops it, the same way a compiler stops code that doesn’t type-check. For teams managing infrastructure across multiple servers, cloud accounts, or clients, that’s the difference between catching a misconfiguration in a plan and explaining it in an incident report.
Drift: the risk nobody wrote down
Policy as code governs the changes that go through the pipeline. It has nothing to say about the changes that don’t.
Go back to the 2:00 AM firewall port. That change happened outside Terraform entirely: someone logged in, made a manual fix, and moved on. The Terraform state file still describes the old, correct configuration. The actual server no longer matches it. This mismatch is called drift, and it’s one of the most common ways a legitimate emergency fix turns into a permanent, undocumented security hole.
Drift is a security problem because it breaks the basic assumption code review depends on: that the infrastructure running in production is the infrastructure that was reviewed and approved. Once drift creeps in, nobody can answer a simple audit question like "what firewall rules exist on this server, and who approved them" with any confidence. That question comes up constantly during SOC 2 and ISO 27001 audits, and "we’re not entirely sure" is not an answer that satisfies an auditor.
Continuous drift detection closes that gap by comparing the real state of infrastructure against the code on a schedule, not just when someone remembers to check. Platforms like Spacelift run that comparison automatically: when a mismatch turns up, it gets surfaced immediately, with the option to reconcile it back into code or roll the infrastructure back to match. Either way, the gap closes in days, not at the next audit.
Access control matters as much as the pipeline itself
None of this works if anyone can approve their own infrastructure changes. Role-based access control, applied to who can propose, approve, and apply infrastructure changes, combined with an audit trail of who did what and when, is what turns policy as code and drift detection into something a compliance team can point to. Without them, the tooling is sound and there is no record that it was used correctly. A policy engine that blocked a bad plan on a Tuesday is only useful evidence if you can show, months later, that it actually ran and who was notified when it did.
Start with the highest-risk changes
You don’t need to lock down every Terraform module on day one. Start with the changes that carry the most risk: identity permissions, network access rules, and anything touching production data. Write policy as code checks for those first, add drift detection on the resources where an undocumented change would actually cause harm, and expand from there.
The server was never the only thing worth securing. The pipeline that builds it deserves the same scrutiny, because that is usually where the real gap sits.