What is Security as Code and how can it improve security operations?

Here at UserZoom, we like to go from good to great! That’s why we took the best parts of Infrastructure as Code and created a new concept for security operations...

What is Security as Code?

Companies are constantly evolving. In a world where Agile is gaining momentum and delivering fast is a must, sometimes security is compromised along the way. Keeping track of all the improvements, having a sturdy change management procedure, and keeping up with the speed seems almost impossible.

Thanks to Infrastructure as Code it is possible to design all the infrastructure settings, changes, migrations, and much more through code.

The benefits are clear: a single source of truth of the required configurations, reducing the cost of deploying the same infrastructure in different scenarios, bring down human errors, as well as the added robustness of managing everything with code; having code reviews, approvals, blames, and all the amazing git features you want!

Here at UserZoom, we like to go from good to great! That’s why we took the best parts of Infrastructure as Code and created a new concept for security operations, I will let you guess… yeah it’s Security as Code! The main concept still remains the same, to be able to deploy security measures, rules, access controls, and alerting systems through code... but there's more.

An excellent use case may be a scenario where multiple AWS accounts need to be managed and secure in a similar way. Having to deal with manual changes, tracking which account has what rules enabled, and not being able to get the whole picture of the security state of your accounts used to be a nightmare. Thanks to Security as Code we just need to ensure the code reflects our compliance and security policies and then that’s it! (Almost... there’s a lot of magic to be done before that.)

Terraform for the win

We use Terraform to manage the security infrastructure. It has a huge community of developers and is constantly evolving, which also makes it challenging. There are certain AWS services that we need to have configured in the same way across all the AWS accounts, for example, AWS Config, AWS Guardduty, Cloudtrail, VPC flow logs, etc. Basically monitoring and alerting resources. The bread and butter of the security teams.

Example schematics of AWS Security Services

There are many different ways to work with Terraform and organize a project. We decided to go with an environment-branching-git-flow approach. So basically we have a branch for each environment we have. That way, we know for sure what changes are deployed where reducing human errors and configuration deviations from the code. And for the development, we went with the classic git-flow process. 

So, as an example... If we wanted to deploy a new AWS config rule on all the accounts we would first checkout master, work on a feature branch and open a PR to a DEV environment. Once tested and deployed, we would open a new PR to a STAGING environment, and eventually a PR against MASTER with those changes. For hotfixes, we would merge to MASTER and then propagate the changes into the different environments via PR.

Source: https://github.com/SvanBoxel/release-based-workflow/issues/1

Another common scenario where we use Terraform is to manage our source code settings in GitHub.

Imagine you have a GitHub organization with 100 repositories. Imagine that your CTO requests that you implement some changes in order to be compliant with the new legislation. In the good old days, you would have to go one by one and manually change all the settings. You could speed things up using the GitHub API, but then how could you enforce that nobody changed those settings? How could you reflect in your change management process that configuration request? What if you need to add external collaborators to only specific repositories, you would need to do that manually, right?

All these problems are solved using terraform to define the GitHub infrastructure! It also helps small security teams to ensure the security policies are enforced. Nobody will bypass your security controls without encountering a smart security engineer ready to smash some policies.

It also empowers the developers to be able to request access using the language that is flowing through their veins, code. Instead of sending an email, Slack, calling a busy security engineer to request access, and slowing the process, they just need to create a PR with the accesses they need. The corresponding code reviewers approve or deny the request, add discussions, interact with the requester all in the same place where it is going to be implemented.

Spice things up with CI/CD

Can we make this process even a little better? Why not add some checks into the changes we are making in our code? We would not want to introduce bugs or malicious code into what’s supposed to be security as code. Thus we rely on GitHub actions to make those checks and then automatically apply the required changes. Here is an example of the workflow for the Pull Requests:

name: Terraform Plan Security
      - main
      - ‘security/**’
    working-directory: security
    name: Workflow
    runs-on: ubuntu-latest
    - name: Checkout Repository
      uses: actions/checkout@master
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
        role-duration-seconds: 1200
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
    - name: Terraform Fmt
      id: fmt
      run: terraform fmt -check -diff
      continue-on-error: true
    - name: Terraform Init
      id: init
      run: terraform init
      continue-on-error: true
    - name: Terraform Validate
      id: validate
      run: terraform validate -no-color
      continue-on-error: true
    - name: Terraform Plan
      id: plan
      run: |
        terraform plan -no-color;
      continue-on-error: true
    - name: ‘End results’
      shell: bash
      run: |
        echo fmt
        test ${{ steps.fmt.outputs.exitcode }} -eq 0
        echo init
        test ${{ steps.init.outputs.exitcode }} -eq 0
        echo validate
        test ${{ steps.validate.outputs.exitcode }} -eq 0

We would basically do the same for the terraform apply but thanks to this action we can ensure that the code has been validated, we will see the plan and its changes and we then can review and approve code based on those checks. 


Thanks to Security as Code we have reduced the amount of time and resources dedicated to making sure we are compliant with our security policies whilst implementing a more resilient procedure.

Of course, there are always going to be ways to bypass these restrictions, but having a good change procedure will make those deviations visible, which is half of the battle in any defense team!

References and more ideas

What is Security as Code? Download the white paper