SecureWorld News

Policy-as-Code Implementation in Secure SDLC

Written by Derek Fisher | Wed | Jul 9, 2025 | 6:30 PM Z

We have a lot of terms in application and product security that help us to either complicate or demystify the activities in pursuit of a secure design. One of those terms that we often use is "secure by design." In a nutshell, secure by design means integrating security into the fabric of the product design where threat management becomes a proactive effort, architecture follows best security practices, features are designed to minimize the attack surface, and the product fails-safe when in a broken state. While there are different ways to implement the secure by design principles, ideally we want to codify and build it into the design lifecycle as seamlessly as possible.

Recently, I saw a post from a peer on LinkedIn that covered the PaC (policy-as-code) as a method of building security into the development lifecycle. It covers how to integrate PaC in the SDLC (software development lifecycle) through OPA (open policy agent). Properly implemented, PaC can automate policy enforcement, break down silos between different teams, provide the coveted shift-left paradigm, support audit and compliance, and end world hunger.

Okay, that last part is made up, but the post got me thinking about getting my hands dirty on PaC and setting up a little lab that I can extend over time. I thought I would share that effort.

Let's start with the foundation.

Policy-as-Code: coding security in

A basic principle in security is to deploy systems without over-permissioned accounts. This follows the principle of least privilege and ensures that systems only allow accounts to perform function at the lowest level of access required to perform the function. Easy enough, right? The challenge is that many systems have accounts that allow far more privilege than is required. This comes from misconfiguration, the failure to remove access from an account after a special circumstance, or not removing permission on an account after someone changes roles. We're getting better at managing this through privilege access management and other IAM related security controls, but it still remains a prevalent issue.

Thirty-one percent of all breaches over the past 10 years involved stolen credentials, according to the 2024 Verizon Data Breach Investigations Report (DBIR). Many of these stem from over-permissioned or poorly managed accounts.

How do these issues become a reality? Some of it comes from developers misconfiguring containers, and the lack of more stringent controls in a production environment. Security teams can also miss these configuration nuances, or simply not have the bandwidth to review every docker configuration across sprawling environments. Additionally, configuration settings can be buried in multi-stage builds that are not evident until the containers are deployed to production.

What if we could have a proactive, automated, and designed-in method of ensuring that we don't over-permission accounts in systems? This is just one use-case for PaC. Consider the fact that many Docker containers run as root by default unless configured explicitly otherwise. This root user has full privileges within the container, and in some cases, those privileges can affect the host system where the container is run with elevated flags like "–privileged." An attacker exploiting a vulnerability inside a misconfigured container can gain root-level access and possible control of the host system.

This is where PaC can help. Take a look at this simple example:

services:
  app:
    image: your-secure-image
    user: "1001:1001" # non-root user
    privileged: false # disable privileged
    security_opt:
      - no-new-privileges:true

Of course, this can limit the container's ability to access some legitimate services within the container, so your mileage may vary, and you may need to tweak the configuration. And while this works well for an individual docker-compose file, in a team setting (such as an enterprise), you want to be able to apply these rules and enforce them for any container that runs in an environment. Enter OPA.

Implement PaC with OPA

OPA is a policy engine that can take static policy documents and turn them into enforceable code. OPA decouples policy decisions from your application logic. Instead of hardcoding rules into your services, you write them in Rego, OPA’s declarative policy language. Then, your services query OPA to get decisions like:

  • Can this user access this resource?
  • Is this Kubernetes deployment secure?
  • Does this Terraform plan violate any guardrails?

OPA can be used in API gateways, Kubernetes clusters, CI/CD pipelines, IaC, and microservices allowing for a centralized way to ensure that environments have appropriate controls such as authentication, authorization, data filtering, and more. For example, in a CI/CD a docker file could be created and evaluated against an OPA policy. If the configuration does not meet the policy the build will fail. Otherwise, it will be allowed to proceed.

Simple CICD flow with an OPA check

OPA uses the Rego language, which is a purpose-built, declarative policy language used to evaluate input data such as Kubernetes manifests, API requests, or Terraform plans. Going back to our evaluation of whether root is enabled in a Docker container, we can create this simple OPA policy:

package docker.authz
default allow := false
allow if {
              not is_root_user
}
# Deny if container is configured to run as root (UID 0)
is_root_user if {
              input.Body.HostConfig.User == "0"
}
is_root_user if {
              input.Body.HostConfig.User == "root"
}

Let's look closer at how PaC can be integrated into a secure SDLC.

Policy-as-Code implementation in a secure SDLC

To get the most out of PaC, it needs to be integrated into the software development processes and treated like any other code artifact. This SDLC integration provides a paved road with security guardrails implemented allowing developers to focus more on feature development.

Bonus: This speeds up the development as security teams are less likely to come in late in the process to identify security issues.

Looking at the various SDLC stages, we can add PaC during the build stage. Doing so offers the earliest opportunity for policy enforcement, preventing non-compliant code from progressing through the pipeline. At this stage teams can implement OPA Conftest for validation of their IaC (infrastructure-as-code) templates, Kubernetes manifests, and configuration files.

# CI/CD Pipeline Example (GitHub Actions)
- name: Validate Kubernetes Manifests
  run: |
    conftest verify --policy policies/ manifests/

Additionally, container security policies can be enforced directly during the build processes through image signature verification and automated vulnerability scanning. This codification of policy prevents vulnerable or unsigned images from reaching production environments.

During the testing stage of the SDLC, PaC can be used to make pre-production testing more efficient and validate that the controls are working. Rather than relying on manual security reviews, the policies can validate that the different software components comply with the organization's standards. For example, integration tests that test service-to-service communication should validate that appropriate authentication flows are functioning, and that data transfer between services is secure. More specifically, policies can verify:

  • API endpoints implement proper rate limiting and authentication
  • Database connections use encrypted protocols and appropriate access controls
  • Service meshes enforce network segmentation and traffic encryption
  • Message queues and event streams handle sensitive data according to privacy policies

System tests should incorporate automated validation to ensure applications meet functional requirements, remain stable, and are free of security issues before promotion to a production environment. Additionally, the CI/CD pipeline should be configured to automatically:

  • Block deployments containing dependencies with critical CVEs or outdated base images
  • Verify applications meet regulatory requirements like GDPR data handling, HIPAA access controls, or PCI DSS encryption standards
  • Ensure applications have proper health checks, monitoring instrumentation, and disaster recovery configurations

Can PaC help even after the application is in a running environment? You bet!

Run-time and beyond

Integrating PaC using OPA goes beyond simple scans of the templates and can be used to make decisions during run-time. Here OPA is a decision engine that can evaluate every request, transaction, and system interaction. It then can match that to the organization's policies, making sure that security and compliance are present. Take a simple authorization check as an example. The running application may receive a user request that requires an access control decision. Rather than embedding the authorization logic directly into the application code, the application delegates this to OPA thereby centralizing the authorization mechanism. It will look something like this:


AuthZ check using an OPA policy decision

Here, the application will intercept and gather the information regarding the request and the user to create a JSON policy that it can forward to the OPA decision engine. This information will include data such as user identity, resource details, request context, and environmental data.

The policy is then constructed and sent to OPA for a decision. A sample JSON is below:

{
"user": { "id": "user123", "roles": ["analyst", "finance"],
"department": "accounting" },
"resource": { "type": "customer_data", "classification":
"confidential", "owner": "finance_team" },
"action": "read", "context": { "time": "2025-01-15T14:30:00Z",
"ip_address": "192.168.1.100", "business_hours": true }
}

The policy sent by the application is then evaluated against the OPA policy which validates whether the user is allowed to have access based on the organization's defined policies. OPA makes a decision and sends it back to the application for enforcement. For instance, the response could simply respond with an "allow" or can include additional recommendations or warnings.

This process can take milliseconds to complete and allows for an effective authorization mechanism that can be easily extended to other applications in the organization. An added benefit is that as the risk profile of the organization changes, if authorization changes are needed, it can be done in a single policy.

Where does that leave us?

I plan on delving deeper into the topic of PaC over the next few weeks and am working on setting up a little lab that I want to extend over time. If you found this helpful, let me know. And by all means, please consider adding your input to the lab project. I'm always open to feedback!

This article appeared originally on LinkedIn here.