Shift left security compliance

Most companies have security compliance requirements that you need to take into account when creating your software. Similarly to how you can express infrastructure and tests as code, you can shift left security compliance concerns into your development team. This blog shows how a team I worked in used Ansible in a (GitLab) delivery pipeline to create compliant Amazon Machine Images (AMI) containing our application.


There are institutions that have taken it upon themselves to come up with security benchmarks that companies can start from. For example:

  • The Center for Internet Security (CIS) offers benchmarks for oft-used applications and operations systems.

  • The Defense Information Systems Agency (DISA) offers “technical guidance to lock down information systems/software that might otherwise be vulnerable to a malicious computer attack“ through their Security Technical Implementation Guides (STIGs).

Both contain builds of compliant operating systems that you could use as the basis of your machine image. The company I worked for required CentOS 7, and I went and looked for a CIS benchmark for that. The CIS website has a list of hardened images, but I took a different route for several reasons.

  1. We had to pick from a predefined list of (hardened) in-company images.

  2. I wanted to understand the CIS benchmark and be able to deviate where desirable, for example, if required in order for a COTS application to run.

  3. We had to make sure that once our development team was done making changes, the resulting image could be checked once more for CIS compliance. This was part of the compliance requirement for autonomy, meaning that if your team can prove it can manage compliance, it gets the seal of approval.

seal of approval

A delivery pipeline with compliance as code

We set up a separate pipeline for making compliant AMIs. The delivery pipeline setup is as follows. Non-compliant AMIs can be deployed to Dev.

ami cis scan 1

Because of immutable infrastructure, you can redeploy to Dev until you’re content with your increment (represented by the built AMI). Most likely not every Dev redeploy has a security risk that you want to validate, and you’ll want to weigh the added value of a benchmark against the time it takes. But before propagating your AMI to Acc/Prod, you do want to be sure that it is both functional and compliant. This can be done by sending the AMI through the ami-cis-scan project, which takes the AMI, runs the CIS benchmark on it, and produces a new compliant AMI. That new AMI can then be deployed to the Test environment for functional testing. Before the ami-cis-scan project, each separate application would have its own propagate step, which makes the AMI available in a different AWS account where the Acc and Prod environment resides. In the new setup, the ami-cis-scan pipeline is the only one that has this step, meaning no AMI can go to Prod that hasn’t gone through a CIS benchmark scan.

The AMI CIS scan project

I ended up using the Ansible playbook in for its specific OS support, but there are other implementations around. This playbook does make changes and we found some of them had a negative impact on a few of the applications we intended to ship. The playbook can be run in dry-run mode to stay in control of making changes manually, but after a few tests to understand what it did, we let it make changes.

Ansible tasks in the CIS scan playbook

cis scan ansible tasks

We interpreted each of the steps and adjusted or skipped some in collaboration with the security compliance department. We also made sure to read up on the rationale behind the benchmark to understand what risk was being addressed, so that we could take a responsible alternative decision in situations where the software requirements conflicted with the security measures. For example, the following snippet from section_01_level2.yml makes sure on SELinux is enforced (literally it makes sure enforcement isn’t turned off), which one of the COTS applications we needed to deploy struggled with.

- name: 1.4.1 Enable SELinux in /etc/grub.conf (Scored)
  lineinfile: >
    - scored
    - section1.4
    - section1.4.1

We found that someone had turned off enforcing to restore some application functionality. This meant SELinux was not disabled but also not actually helping to guarantee our security the way it’s supposed to. Some research into SELinux into this setting showed that we were leaving our system needlessly at risk. We also found that there was a more fine-grained approach to making the application work (using semanage permissive).


In essence, this is a shift left of your security compliance concerns. Don’t wait for an audit or assume some security ambassador will come and check once you’re done. Include the requirements in your software delivery process in an automated testable manner.