Enforce Custom Stages in Pipeline Execution Policies
Release notes
We're excited to introduce a new capability for Pipeline Execution Policies (PEP) that allows you to enforce custom stages into your CI/CD pipelines. This feature provides greater flexibility and control over your pipeline structure while maintaining security and compliance requirements.
Benefits
- Enhanced pipeline customization: Define and inject custom stages at specific points in your pipeline, allowing for more granular control over job execution order.
- Improved security and compliance: Ensure that security scans and compliance checks run at the most appropriate times in your pipeline, such as after build but before deployment.
- Flexible policy management: Maintain centralized policy control while allowing development teams to customize their pipelines within defined guardrails.
- Seamless integration: Custom stages work alongside existing project stages and other policy types, providing a non-disruptive way to enhance your CI/CD workflows.
How it works
The new inject_policy_ci
strategy for Pipeline Execution Policies allows you to define custom stages in your policy configuration. These stages are then intelligently merged with your project's existing stages using a Directed Acyclic Graph (DAG) algorithm, ensuring proper ordering and preventing conflicts.
For example, you can now easily inject a custom security scanning stage between your build and deploy stages:
stages: [build, policy-test, deploy]
policy-security-scan-job:
stage: policy-test
script:
- run_security_script
This feature provides a powerful way to standardize and enforce security practices across your projects while maintaining the flexibility needed for diverse development workflows.
The inject_ci
mode will be deprecated, allowing you to opt into the inject_policy_ci
mode. The inject_policy_ci
mode will become the default when configuring policies with Inject
in the policy editor.
Problem to solve
Currently, Pipeline Execution Policies only allow jobs to be placed in the following two stages:
-
.pipeline-policy-pre
which always run at the start of the pipeline. -
.pipeline-policy-post
, which always runs at the end.
In the experimental feature Pipeline execution policy action (no longer available), there as an additional stage (.pipeline-policy-test
) which always ran after the test
stage, but this behavior was not carried over to Pipeline Execution Policies
.
This limitation can affect the needs of some users (for example, those that practice trunk-based development and can't make use of MRAP's) who require more granular control over when policy-enforced jobs run, especially in relation to critical stages like testing and deployment.
Example use case
Consider a workflow in a trunk-based development project in which a deploy
job must run only if the Pipeline Execution Policy jobs are successful and find no vulnerabilities:
graph LR
A[build] --> B[test] --> C[analyze]--> D[deploy]
The deploy
job should only run if the test
jobs run properly and generate security artifacts which are consumed consumed by the analyze
job in order to parse any detected vulnerabilities. If any vulnerabilities are found, the analyze
job fails the pipeline which prevents the vulnerabilities from being deployed.
Ideally, the analyze
job would be enforced via a Pipeline Execution Policy; however, since neither of the two available stages run after the test
job, they cannot fail the pipeline and prevent the changes to being deployed.
graph LR
A[.pipeline-policy-pre] --> B[Build] --> C[Test] --> D[Deploy] --> E[.pipeline-policy-post]
Notice that previously, the .pipeline-policy-pre
stage helped achieving just that, but the feature does no longer implement this stage:
graph LR
A[.pipeline-policy-pre] --> B[Build] --> C[Test] --> D[.pipeline-policy-test] --> E[Deploy] --> F[.pipeline-policy-post]
Proposal
Introduce support for "custom stages" that can be placed in the middle of the pipeline by defining stages sequentially. For example, policy-test
can be placed between test
and deploy
in the project pipeline:
stages: [test, policy-test, deploy]
policy-test-job:
stage: policy-test
script:
- sleep 1
It is possible to specify any possible stages that projects might be using. Only stages with jobs will be present in the resulting pipeline. For example:
stages: [build, compile, test, check, policy-test, deploy, publish]
policy-test-job:
stage: policy-test
script:
- sleep 1
In this case, policy-test
will be placed after build, compile, test, check
and before deploy, publish
. If project was using stages: [compile, check, publish]
, the resulting pipeline would contain stages: [compile, check, policy-test, publish]
.
If policy stages only specify the custom stage, this will be placed at the end of the pipeline, just before .pipeline-policy-post
(if used):
stages: [policy-test]
policy-test-job:
stage: policy-test
script:
- sleep 1
With default project stages, this results in [build, test, deploy, policy-test]
.
When used in a project without .gitlab-ci.yml
, the behavior is the same as injecting into default stages.
Behavior with other policies
- It is additive to other policies and the project
- When used together with
override_project_ci
policies, it merges into the override stages.
New strategy
Users can specify a new pipeline_config_strategy
called inject_policy_ci
which enables custom stages from the policy pipelines to be injected. It is made as a separate strategy so that users can opt-in and avoid breaking changes. inject_ci
will be deprecated and customers will be encouraged to use the new strategy.
Possible pipeline errors
Depending on the policy stages configuration, the pipeline could fail with this strategy - in case the stages are defined as cyclic. Example:
Policy stages: [test, deploy]
Project stages: [build, deploy, test]
In this example, deploy
depends on test
in the policy, but test
depends on deploy
in the project. This configuration results in a "cyclic dependencies" error:
Verification steps
- Create a project
- Create a policy with
pipeline_config_strategy: inject_policy
- Verify the various examples mentioned in the documentation: https://docs.gitlab.com/ee/user/application_security/policies/pipeline_execution_policies.html#stages-injection