Securing the pipeline: CI/CD, Terraform and Ansible
Why this matters
A continuous integration and deployment pipeline is the most privileged system most organisations own and the one least often treated that way. It can build and deploy arbitrary code, it holds credentials to every environment it deploys to, and in an infrastructure-as-code estate it can create, modify and destroy the infrastructure itself. Compromising the pipeline is equivalent to compromising everything it can reach, without touching any of those things directly.
The architectural point CAS-005 is testing is that the pipeline is a trust boundary and a production system, not a developer convenience. Scenarios describe an organisation with strong production controls and a build system anybody can modify, and ask where the weakness is.
There is a second idea that runs through the lesson and is worth naming early: infrastructure as code moves infrastructure decisions into a text file that can be reviewed before it takes effect. That makes code review the cheapest architecture control available — which is a genuine advantage, and it only exists if the review actually happens before the apply.
The lesson
The pipeline as an attack surface: runners, secrets, artefacts and signing
Take the components in turn, because each is a distinct exposure.
Runners and build agents execute arbitrary code — that is their function. Three properties matter: whether a runner is shared between projects of different sensitivity, whether it is ephemeral or persistent, and what it can reach on the network. A persistent shared runner is the highest-value target in the estate, because a compromise of any one project's build reaches every other project's build. Ephemeral runners destroyed after each job remove persistence entirely and are the single strongest control here.
Secrets. The pipeline holds credentials to registries, cloud accounts, package repositories and production. The failure modes are well worn: secrets in environment variables that appear in build logs; secrets available to pull-request builds from forks; long-lived static credentials where short-lived federated ones would work. The direction of travel — and the answer a scenario usually wants — is workload identity: the pipeline authenticates to the cloud provider with a short-lived token bound to the repository and branch, so there is no static credential to steal.
Artefacts. Build output is the thing that reaches production. If the artefact store can be written by anything other than the build, an attacker can replace a verified build with their own and every downstream control passes, because those controls check the pipeline ran rather than what it produced.
Signing closes that. The build signs its artefacts and the deployment verifies the signature, which ties what runs to what was built. Its value depends entirely on protecting the signing key — a key sitting in the same pipeline that can be modified by anyone provides no assurance at all, which is the trap scenarios set.
Two structural controls appear repeatedly as correct answers: separate the build identity from the deploy identity, so a compromised build cannot deploy directly; and protect the pipeline definition itself, since in most systems the pipeline is configured by a file in the repository, and whoever can merge to that file controls the pipeline.
Infrastructure as code review as the cheapest architecture control there is
When infrastructure is declared in code, a change to a firewall rule, a storage permission or an identity policy arrives as a diff — reviewable, comparable against a standard, and rejectable before it exists.
That is a substantial architectural gain over consoles, where the change happens at the moment of clicking and review can only be retrospective. Three things follow.
Review catches design defects, not just defects. A diff that adds an ingress rule from anywhere, grants a wildcard permission, disables encryption, or creates a public endpoint is visible to a reviewer who knows what to look for. These are the findings a scanner produces weeks later and a penetration test produces months later.
The review must be enforced. Branch protection requiring approval, and a deployment identity that can only apply what was merged, are what make the control real. A pipeline that will apply an unreviewed branch turns the whole advantage off.
Review needs a checklist, because reviewers optimise for whether the change works. A short, standing list — network exposure, identity scope, encryption, logging, data location, deletion protection — catches most of what matters and is cheap to apply.
The honest limitation, which scenarios test: code review sees the declared state, and reality can differ. That is drift, and it is the subject of a later lesson in domain 3. Review plus drift detection is the pair; either alone leaves a gap.
Terraform state as a secret store nobody classified, and how it leaks
Declarative infrastructure tools keep a state file recording what they created and its current attributes, so they can compute the difference on the next run. That file is far more sensitive than its role suggests.
It commonly contains resource identifiers, network topology, and — this is the part that surprises people — attribute values including secrets: generated passwords, connection strings, keys, and any sensitive value passed as an input. Marking a variable sensitive suppresses it from console output and does not remove it from state.
So the state file is, in practice, an unclassified secret store, and it leaks in predictable ways: committed to a repository because it appeared in the working directory; stored in an object store with default permissions; copied to a laptop for a local run; and present in every backup of all of the above.
The controls are specific and appear as correct answers:
- Remote state in a dedicated, access-controlled backend, encrypted at rest with a managed key, with versioning and its own audit log.
- State locking, so two concurrent applies cannot corrupt it — an availability control that also prevents a class of race conditions.
- Access to state restricted to the pipeline identity, not to every engineer, because read access to state is read access to the secrets in it.
- Secrets sourced at apply time from a secrets manager rather than passed as inputs, which reduces what lands in state in the first place.
- The file ignored by version control, enforced rather than assumed.
Treat state as production data of the highest classification the estate holds. That is the sentence to carry.
Ansible and agentless configuration: credentials, vaults and idempotence
Procedural configuration management connects to hosts and executes tasks. The common agentless model has a distinctive security profile.
The control node is a privileged hub. It holds credentials that can reach every managed host, usually with administrative rights. Compromising it is equivalent to compromising the fleet, which makes it a system requiring the same protection as a domain controller — a comparison worth holding, because organisations routinely run it on an engineer's workstation.
Credentials are the central problem. The good pattern is per-host or per-group keys with restricted commands, short-lived certificates where the platform supports them, and no shared administrative password. The common bad pattern is one key that opens everything, held on the control node and copied to wherever anyone runs playbooks.
Vault files encrypt secrets at rest inside the repository, which is better than plaintext and is not the same as a secrets manager: the decryption key must still be available wherever the playbook runs, and rotation means re-encrypting and re-committing. Where a proper secrets manager is available, sourcing at run time is stronger.
Idempotence is a security property, not only an operational one. A playbook that is idempotent can be run repeatedly to enforce a state, which converts configuration management into a continuous control that corrects drift. A playbook that is not — one that appends to a file, or that only works on a fresh host — cannot be used that way, so the control degrades into a one-time deployment step.
Two habits round it out: run in check mode first against production, so the diff is seen before it is applied; and treat playbooks as code subject to the same review as the infrastructure definitions above, since a playbook can do anything the control node's credentials permit.
Policy as code: refusing a non-compliant plan before it is applied
The strongest control in this lesson, and the one that turns standards into enforcement.
Policy as code expresses organisational rules as machine-evaluable policy — encryption required on storage, no unrestricted ingress, resources must carry an owner tag, only approved regions, no public object storage — and evaluates them against the plan the infrastructure tool produces before anything is created. A violation fails the pipeline.
Why this is architecturally significant:
- It moves enforcement left of creation. Detective controls find the public bucket after it exists and after anything watching has had the chance to find it. Policy as code prevents its existence.
- It makes the standard executable. The written standard from lesson one and the enforcing policy are the same artefact, so they cannot drift apart — which is the recurring failure of written controls.
- It is testable and reviewable. Policies are code: version-controlled, reviewed, and covered by tests that prove each policy rejects what it is meant to reject. That is the same canary discipline this whole library applies to checks, and a policy nobody proved can fail is a policy nobody should trust.
- It scales. One policy applies to every pipeline, so a new team inherits the estate's rules on their first deployment rather than learning them by failing an audit.
Two design decisions decide whether it works in practice. Exceptions must be possible and recorded — a policy engine with no exception path is bypassed by disabling it, so the exception must route to the register from lesson one with an owner and an expiry. And policies should start in warning mode, becoming blocking once the existing violations are remediated, because a policy that fails every pipeline on the day it ships is switched off on the same day.
Complementary enforcement exists at the platform level — organisational guardrails and service control policies that constrain what an account may do regardless of how the request arrives. Policy as code catches it in the pipeline; platform guardrails catch it whatever route it took. Both, and domain 2's next lessons return to that pairing.
Practise what you just read
1. Why is a continuous integration pipeline described as the most privileged system an organisation owns?
Select one
Show answer
B. In an infrastructure-as-code estate it can also create, modify and destroy the infrastructure itself. Compromising it is equivalent to compromising everything it reaches, without touching any of those things directly.
2. Marking a Terraform variable as sensitive achieves what?
Select one
Show answer
C. The value remains in state in plaintext. State commonly contains generated passwords, connection strings and keys, which makes it an unclassified secret store that leaks through repositories, object stores and every backup of both.
3. Which runner configuration is the strongest control against persistence?
Select one
Show answer
D. A persistent shared runner is the highest-value target in the estate, because a compromise of any one project build reaches every other project build. Destroying the runner removes persistence entirely.
10 more questions on this objective are part of the full course.
Hands-on labs
Part of the free CompTIA SecurityX CAS-005 course — 49 lessons and 77 hands-on labs.
This is an independent study companion for CompTIA SecurityX CAS-005 and is not produced by or endorsed by CompTIA.