Policy Engine Plugin for Spinnaker or Armory Continuous Deployment

Empower DevOps efforts and ensure compliance with Armory Policy Engine. You can enforce save time, runtime, and user interaction policies for Spinnaker or Armory Continuous Deployment. Ensure verification at runtime and throughout your lifecycle. Enforce trusted standards to reduce risk and scale DevOps efforts. Support workflow and collaborate without causing breaking changes.

Proprietary

Overview of the Armory Policy Engine

The Armory Policy Engine is a proprietary feature for Armory Continuous Deployment and open source Spinnaker. It is designed to enable more complete control of your software delivery process by providing you with the hooks necessary to make assertions about the structure and behavior of pipelines and processes in your environment. The Policy Engine uses the Open Policy Agent (OPA) and input style documents to perform validations on the following:

  • Save time validation - Validate pipelines as they’re created or modified. Tasks with no policies are not validated.
  • Runtime validation - Validate deployments as a pipeline is executing. Tasks with no policies are not validated.
  • Entitlements using API Authorization - Enforce restrictions on who can perform certain actions. Note that if you enable policies for API authorization, you must configure who can make API calls or else the API service (Gate) rejects all API calls.

If no policies are configured for these policy checks, all actions are allowed.

At a high level, adding policies for the Policy Engine to use is a two-step process:

  1. Create the policies and save them to a .rego file.
  2. Add the policies to the OPA server with a ConfigMap or API request.

These policies are evaluated against the packages that Armory Continuous Deployment sends between its services. For a list of packages that you can write policies against, see Policy Engine Packages.

Enabling the Policy Engine consists of the following steps:

  1. Deploy an OPA server
  2. Enable the Policy Engine plugin

Compatibility

Armory CD (Spinnaker) VersionArmory Policy Agent Plugin Version
2.28.x (1.28.x)0.3.0
2.27.x (1.27.x)0.2.2
2.26.x (1.26.x)0.1.6

OPA requirements

The Policy Engine requires an Open Policy Agent server. This can be deployed in the same cluster as Armory Continuous Deployment or in an external cluster.

The following table lists the requirements:

RequirementVersionNote
OPA Server0.12.x or laterSpecifically, the v1 API must be available. When you specify the OPA server URL in the Armory Continuous Deployment configs, include v1 in the URL: http://<your-opa-server>:<port>/v1.

Supported validations

ValidationArmory Continuous Deployment VersionNote
Save time validationAll supported versionsIf no policies are set, you cannot save any pipelines until you set any policy or turn off save time validation.
Runtime validationAll supported versionsIf no policies are set, no policy enforcement occurs and pipelines run as they do normally.

Before you begin

  • The Policy Engine requires an Open Policy Agent (OPA) server, version 0.12.x or later. You can use the example on this page to deploy a server in the same Kubernetes cluster as Armory Continuous Deployment. Alternately, see the OPA documentation for information about how to deploy an OPA server. Specifically, the OPA v1 API must be available.
  • You have identified the Policy Engine plugin that is compatible with your version of Armory Continuous Deployment or Spinnaker. See the Supported versions section.
  • If you are using Spinnaker, you are familiar with installing and using plugins in Spinnaker.

Deploy an OPA server

The Policy Engine supports the following OPA server deployments:

  • An OPA server deployed in the same Kubernetes cluster as an Armory CD or Spinnaker deployment. The Use ConfigMaps for Open Policy Agent policies section contains a ConfigMap you can use.
  • An OPA cluster that is not in the same Kubernetes cluster as an Armory CD or Spinnaker deployment. See the OPA documentation for more information about installing an OPA server in a separate cluster.

Use ConfigMaps for Open Policy Agent policies

If you want to use ConfigMaps for OPA policies, you can use the following manifest as a starting point. This example manifest deploys an OPA server and applies the configuration for things like RoleBinding and a static DNS. When using the example, keep the following guidelines in mind:

  • The manifest does not configure any authorization requirements for the OPA server it deploys. This means that anyone can add a policy.
  • The manifest deploys the OPA server to a namespace called opa.
  • The OPA server uses "--require-policy-label=true". This configures the OPA server to look for a specific label so that it does not check all ConfigMaps for new policies. For information about how to apply the relevant label to your policy ConfigMaps, see Add the policy to your OPA server.
Show the manifest
---
apiVersion: v1
kind: Namespace
metadata:
  name: opa # Change this to install OPA in a different namespace
---
# Grant service accounts in the 'opa' namespace read-only access to resources.
# This lets OPA/kube-mgmt replicate resources into OPA so they can be used in policies.
# The subject name should be `system:serviceaccounts:` where `` is the namespace where OPA will be installed
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: opa-viewer-spinnaker
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
  name: system:serviceaccounts:opa # Change this to the namespace OPA is installed in
  apiGroup: rbac.authorization.k8s.io
---
# Define role in the `opa` namespace for OPA/kube-mgmt to update configmaps with policy status.
# The namespace for this should be the namespace where policy configmaps will be created
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: opa # Change this to the namespace where policies will live
  name: configmap-modifier
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["update", "patch"]
---
# Bind the above role to all service accounts in the `opa` namespace
# The namespace for this should be the namespace where policy configmaps will be created
# The subject name should be `system:serviceaccounts:` where `` is the namespace where OPA will be installed
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: opa # Change this to the namespace where policies will live
  name: opa-configmap-modifier
roleRef:
  kind: Role
  name: configmap-modifier
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
  name: system:serviceaccounts:opa # Change this to the namespace OPA is installed in
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: opa-deployment
  namespace: opa # Change this to the namespace OPA is installed in
  labels:
    app: opa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: opa
  template:
    metadata:
      labels:
        app: opa
    spec:
      containers:
      # WARNING: OPA is NOT running with an authorization policy configured. This
      # means that clients can read and write policies in OPA. If you are
      # deploying OPA in an insecure environment, be sure to configure
      # authentication and authorization on the daemon. See the Security page for
      # details: https://www.openpolicyagent.org/docs/security.html.
        - name: opa
          image: openpolicyagent/opa:0.28.0
          args:
            - "run"
            - "--server"
            - "--addr=http://0.0.0.0:8181"
          readinessProbe:
            httpGet:
              path: /health
              scheme: HTTP
              port: 8181
            initialDelaySeconds: 3
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              scheme: HTTP
              port: 8181
            initialDelaySeconds: 3
            periodSeconds: 5
        - name: kube-mgmt
          image: openpolicyagent/kube-mgmt:0.9
          args:
          # Change this to the namespace where you want OPA to look for policies
            - "--policies=opa"
          # Configure the OPA server to only check ConfigMaps with the relevant label
            - "--require-policy-label=true"
---
# Create a static DNS endpoint for Spinnaker to reach OPA
apiVersion: v1
kind: Service
metadata:
  name: opa
  namespace: opa # Change this to the namespace OPA is installed in
spec:
  selector:
    app: opa
  ports:
  - protocol: TCP
    port: 8181
    targetPort: 8181

Install the Policy Engine plugin

You have the following options for installing the Policy Engine plugin:

  • Armory CD: Armory Operator
  • Spinnaker: Spinnaker Operator, Local Config, or Halyard

You can install the Policy Engine plugin using the Spinnaker Operator and the sample manifest, which uses Kustomize and is in the spinnaker-kustomize-patches repository.

Show the manifest
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component

patchesStrategicMerge:
  - feature.yml
  - patch-labels.yaml

configMapGenerator:
  # ConfigMap holding OPA policy definitions for use by Armory's Policy Engine. Required by policy-engine-plugin
  - name: spin-policies
    options:
      disableNameSuffixHash: true
    files:
      - policies/manual-judgement.rego
      - policies/pipeline-trigger.rego

Note: The sample manifest is for the Armory Operator and Armory CD. When using the Spinnaker Operator and Spinnaker, you must replace the apiVersion value “spinnaker.armory.io/” with “spinnaker.io/”. For example:

  • Armory Operator: apiVersion: spinnaker.armory.io/v1alpha2
  • Spinnaker Operator: apiVersion: spinnaker.io/v1alpha2

This patch uses YAML anchors to ensure that plugin versions are set correctly throughout Spinnaker’s config. In the gate.spinnaker.extensibility.deck-proxy.plugins.Armory.PolicyEngine.version entry, make sure to replace the version number listed after &version with the version of the plugin you want to use. Refer to the supported versions section to determine the correct plugin version for your installation.

Apply the manifest using kubectl.

You can enable the Policy Engine plugin using the Armory Operator and the sample manifest, which uses Kustomize and is in the spinnaker-kustomize-patches repository.

Show the manifest
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component

patchesStrategicMerge:
  - feature.yml
  - patch-labels.yaml

configMapGenerator:
  # ConfigMap holding OPA policy definitions for use by Armory's Policy Engine. Required by policy-engine-plugin
  - name: spin-policies
    options:
      disableNameSuffixHash: true
    files:
      - policies/manual-judgement.rego
      - policies/pipeline-trigger.rego

This patch uses YAML anchors to ensure that plugin versions are set correctly throughout Armory CD’s config. In the gate.spinnaker.extensibility.deck-proxy.plugins.Armory.PolicyEngine.version entry, make sure to replace the version number listed after &version with the version of the plugin you want to use. Refer to the supported versions section to determine the correct plugin version for your installation.

Apply the manifest using kubectl.

The Policy Engine plugin extends Orca, Gate, Front50, Clouddriver, and Deck. You should create or update the extended service’s local profile in the same directory as the other Halyard configuration files. This is usually ~/.hal/default/profiles on the machine where Halyard is running.

  1. Add the following to gate-local.yml, orca-local.yml, front50-local.yml, and clouddriver.yml:

    armory:
       policyEngine:
          failOpen: false
          opa:
             # Replace with the actual URL to your Open Policy Agent deployment
             baseUrl: http://localhost:8181/v1/data
             # Optional. The number of seconds that the Policy Engine will wait for a response from the OPA server. Default is 10 seconds if omitted.
             # timeoutSeconds: <integer>
     spinnaker:
        extensibility:
           plugins:
              Armory.PolicyEngine:
                 enabled: true
              policyEngine:
                 url: https://raw.githubusercontent.com/armory-plugins/pluginRepository/master/repositories.json
    
  2. Save your files and apply your changes by running hal deploy apply.

  1. Add the plugins repository

    hal plugins repository add policyEngine \
     --url=https://raw.githubusercontent.com/armory-plugins/pluginRepository/master/repositories.json
    

    This creates the following entry in your .hal/config:

    repositories:
       policyEngine:
          enabled: true
          url: https://raw.githubusercontent.com/armory-plugins/pluginRepository/master/repositories.json
    
  2. Add the plugin

    Be sure to replace <version> with the version that’s compatible with your Spinnaker instance.

    hal plugins add Armory.PolicyEngine --version=<version> --enabled=true
    

    If you specified version 0.2.2, Halyard creates the following entry in your .hal/config file:

    spinnaker:
       extensibility:
          plugins:
             Armory.PolicyEngine:
                id: Armory.PolicyEngine
                enabled: true
                version: 0.2.2
    
  3. Apply the changes by executing hal deploy apply.

Timeout settings

You can configure the amount of time that the Policy Engine waits for a response from your OPA server. If you have network or latency issues, increasing the timeout can make Policy Engine more resilient. Use spec.spinnakerConfig.profiles.spinnaker.armory.policyEngine.opa.timeoutSeconds to set the timeout in seconds. The default timeout is 10 seconds if you omit the config.

What’s next


Last modified August 18, 2023: (02b163b7)