Harness Hardening Guide
Software delivery platform hardening for Harness including SAML SSO, RBAC, secret management, and pipeline security
Overview
Harness is a leading software delivery platform providing CI/CD, feature flags, cloud cost management, and service reliability. As a platform managing deployments and infrastructure access, Harness security configurations directly impact software supply chain security.
Intended Audience
- Security engineers managing DevOps platforms
- Platform engineers configuring Harness
- DevOps teams managing pipelines
- GRC professionals assessing CI/CD security
How to Use This Guide
- L1 (Crawl): Essential controls for all organizations
- L2 (Walk): Enhanced controls for security-sensitive environments
- L3 (Run): Strictest controls for regulated industries
Scope
This guide covers Harness security including SAML SSO, RBAC, secret management, and pipeline governance.
Table of Contents
1. Authentication & SSO
1.1 Configure SAML Single Sign-On
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 6.3, 12.5 |
| NIST 800-53 | IA-2, IA-8 |
Description
Configure SAML SSO to centralize authentication for Harness users.
Rationale
Why This Matters:
- Centralizes Harness authentication in your corporate IdP, enforcing MFA, conditional access, and a consistent password policy on every login
- Harness controls deployment pipelines, infrastructure connectors, and cloud credentials, so a single compromised login can push malicious code to production
- IdP-driven provisioning deprovisions departed users automatically, eliminating orphaned accounts with standing pipeline access
- Local Harness logins bypass IdP controls and become a soft target for credential stuffing and phishing
Attack Prevented: Credential theft, phishing, credential stuffing, orphaned-account access
Prerequisites
- Harness admin access
- SAML 2.0 compatible IdP
- Enterprise tier (for some features)
ClickOps Implementation
Step 1: Access Authentication Settings
- Navigate to: Account Settings → Authentication
- Select SAML Provider
Step 2: Configure SAML
- Click Add SAML Provider
- Configure IdP settings:
- Entity ID
- SSO URL
- Certificate
- Configure group mappings
Step 3: Test and Enable
- Test SSO authentication
- Configure SSO enforcement
- Document admin fallback
Time to Complete: ~1-2 hours
1.2 Enforce Two-Factor Authentication
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 6.5 |
| NIST 800-53 | IA-2(1) |
Description
Require 2FA for all Harness users.
Rationale
Why This Matters:
- Adds a second factor so a stolen or guessed password alone cannot grant access to deployment controls
- Harness operators can trigger production deployments and reference pipeline secrets, so a single-factor compromise exposes the whole delivery chain
- Phishing-resistant MFA for admins blocks real-time credential relay and MFA-fatigue attacks
- Enforcing 2FA at the IdP covers all SSO users consistently instead of relying on per-user opt-in
Attack Prevented: Password reuse, credential stuffing, phishing, account takeover, MFA fatigue
ClickOps Implementation
Prerequisite: an account admin must enable 2FA on their own Harness profile before they can enforce it account-wide — Harness prompts the admin to protect their own login first (Harness two-factor authentication docs).
Step 1: Enable 2FA Requirement
- Navigate to: Account Settings → Authentication
- Enable Enforce Two Factor Authentication
- Configure enforcement policy
Interaction with per-user 2FA: Harness sends a 2FA challenge if one or both of the account-level enforcement setting and the individual user’s own 2FA setting are enabled. Disabling account-level enforcement therefore does not disable 2FA for users who enabled it on their own profile — audit both surfaces when reasoning about coverage. Source: Harness two-factor authentication docs.
Step 2: Configure via IdP
- Enable MFA in identity provider
- Use phishing-resistant methods for admins
- All SSO users subject to IdP MFA
Code Implementation
Code Pack: Terraform
# Enforce two-factor authentication at the account level.
# Note: Harness 2FA enforcement is managed via Account Settings > Authentication
# in the UI. The Terraform provider manages this through account-level settings.
# The harness_platform_usergroup resources below ensure that all groups
# require SSO (which includes IdP-enforced MFA).
# Admin group with SSO-enforced MFA
resource "harness_platform_usergroup" "mfa_enforced_admins" {
identifier = "mfa_enforced_admins"
name = "MFA Enforced Administrators"
linked_sso_type = "SAML"
externally_managed = true
sso_linked = true
notification_configs {
type = "EMAIL"
send_email_to_all = true
}
}
# Service account for automation (exempt from interactive MFA, uses SAT)
resource "harness_platform_service_account" "automation" {
identifier = "hth_automation_sa"
name = "HTH Automation Service Account"
description = "Service account for automated operations -- authenticates via SAT, not interactive MFA"
email = "automation@harness.local"
account_id = var.harness_account_id
}
1.3 Configure IP Allowlisting
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 13.5 |
| NIST 800-53 | AC-17 |
Description
Restrict access to approved IP ranges.
Rationale
Why This Matters:
- Restricts Harness console and API access to known corporate or VPN egress ranges, shrinking the externally reachable attack surface
- Even if credentials or session tokens are stolen, an attacker outside the allowed ranges cannot reach the platform
- Confines automation and API tokens used in CI to their expected network locations
- Adds a network-layer control that complements identity controls for defense in depth
Attack Prevented: Stolen-credential reuse from untrusted networks, token replay, unauthorized API access
ClickOps Implementation
Step 1: Configure IP Allowlist
- Navigate to: Account Settings → Security
- Enable IP allowlisting
- Add approved IP ranges
Code Implementation
Code Pack: Terraform
# IP allowlist restricts platform access to approved network ranges.
# Only applied at L2+ profile levels.
resource "harness_platform_ip_allowlist" "corporate" {
count = var.profile_level >= 2 && length(var.allowed_source_cidrs) > 0 ? 1 : 0
identifier = "hth_corporate_allowlist"
name = var.ip_allowlist_name
description = "Restrict access to approved corporate IP ranges (HTH Control 1.3)"
allowed_source_type = "IP_ADDRESS"
ip_address = var.allowed_source_cidrs[0]
enabled = true
}
# Additional allowlist entries for each CIDR beyond the first
resource "harness_platform_ip_allowlist" "corporate_additional" {
count = var.profile_level >= 2 ? max(length(var.allowed_source_cidrs) - 1, 0) : 0
identifier = "hth_corporate_allowlist_${count.index + 1}"
name = "${var.ip_allowlist_name} ${count.index + 2}"
description = "Additional corporate IP range ${count.index + 2} (HTH Control 1.3)"
allowed_source_type = "IP_ADDRESS"
ip_address = var.allowed_source_cidrs[count.index + 1]
enabled = true
}
2. Access Controls
2.1 Configure Role-Based Access Control
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6 |
Description
Implement least privilege using Harness RBAC.
Rationale
Why This Matters:
- Least-privilege roles ensure users and service accounts can only act on the pipelines, secrets, and connectors they actually need
- Custom roles paired with resource groups prevent the over-broad standing access that turns one compromised account into account-wide control
- Scoping limits blast radius — a developer role cannot modify production governance or read every secret
- Regular access reviews catch privilege creep before it becomes an audit or breach finding
Attack Prevented: Privilege escalation, lateral movement, insider misuse, excessive standing access
ClickOps Implementation
Step 1: Review Roles
- Navigate to: Account Settings → Access Control → Roles
- Review predefined roles:
- Account Admin
- Organization Admin
- Project Admin
- Pipeline Executor
- Create custom roles
Step 2: Configure Resource Groups
- Define resource groups
- Scope access to specific resources
- Apply least privilege
Step 3: Assign Permissions
- Assign roles to users/groups
- Use resource groups for scoping
- Regular access reviews
Code Implementation
Code Pack: Terraform
# Custom least-privilege roles for pipeline operators
resource "harness_platform_roles" "pipeline_executor" {
identifier = "hth_pipeline_executor"
name = "Pipeline Executor"
description = "Least-privilege role for executing pipelines without admin access (HTH Control 2.1)"
permissions = [
"core_pipeline_view",
"core_pipeline_execute",
"core_service_view",
"core_environment_view",
"core_connector_view"
]
allowed_scope_levels = ["project"]
}
# Read-only viewer role for auditors and observers
resource "harness_platform_roles" "viewer" {
identifier = "hth_viewer"
name = "Read-Only Viewer"
description = "Read-only role for auditors and observers (HTH Control 2.1)"
permissions = [
"core_pipeline_view",
"core_service_view",
"core_environment_view",
"core_connector_view",
"core_secret_view"
]
allowed_scope_levels = ["project"]
}
# Custom roles from variable input
resource "harness_platform_roles" "custom" {
for_each = var.custom_roles
identifier = each.key
name = each.value.name
description = "Custom role managed by HTH Code Pack (Control 2.1)"
permissions = each.value.permissions
allowed_scope_levels = each.value.allowed_scope_levels
}
# Resource group scoping pipeline resources at the project level
resource "harness_platform_resource_group" "project_pipelines" {
identifier = "hth_project_pipelines"
name = "Project Pipelines"
description = "Resource group scoped to pipeline resources within a project (HTH Control 2.1)"
account_id = var.harness_account_id
included_scopes {
filter = "INCLUDING_CHILD_SCOPES"
}
resource_filter {
include_all_resources = false
resources {
resource_type = "PIPELINE"
}
resources {
resource_type = "SERVICE"
}
resources {
resource_type = "ENVIRONMENT"
}
}
}
2.2 Configure Organization/Project Hierarchy
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6 |
Description
Use hierarchy for access isolation.
Rationale
Why This Matters:
- Separating business units, and especially production from development, into distinct organizations and projects enforces tenant isolation
- Scoped project-level access prevents a developer on one team from touching another team’s pipelines, connectors, or secrets
- Hierarchy creates clear boundaries for least privilege and for auditing cross-project access
- Isolation contains the blast radius of a compromised account or misconfigured role to a single scope
Attack Prevented: Cross-project access, lateral movement, accidental or malicious production changes
ClickOps Implementation
Step 1: Define Organization Structure
- Create organizations for business units
- Create projects within organizations
- Separate production and development
Step 2: Configure Scoped Access
- Assign users at appropriate level
- Use project-level access for least privilege
- Audit cross-project access
Code Implementation
Code Pack: Terraform
# Organizations for business unit isolation (L2+)
resource "harness_platform_organization" "org" {
for_each = var.profile_level >= 2 ? var.organizations : {}
identifier = each.key
name = each.value.name
description = each.value.description != "" ? each.value.description : "Organization managed by HTH Code Pack (Control 2.2)"
}
# Projects within organizations for environment isolation (L2+)
resource "harness_platform_project" "project" {
for_each = var.profile_level >= 2 ? var.projects : {}
identifier = each.key
name = each.value.name
org_id = each.value.org_id
color = each.value.color
depends_on = [harness_platform_organization.org]
}
# Resource group scoped to organization level for org-level access control
resource "harness_platform_resource_group" "org_scoped" {
for_each = var.profile_level >= 2 ? var.organizations : {}
identifier = "hth_org_${each.key}"
name = "${each.value.name} Resources"
description = "Organization-scoped resource group for ${each.value.name} (HTH Control 2.2)"
account_id = var.harness_account_id
org_id = each.key
included_scopes {
filter = "INCLUDING_CHILD_SCOPES"
org_id = each.key
}
resource_filter {
include_all_resources = true
}
depends_on = [harness_platform_organization.org]
}
2.3 Limit Admin Access
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6(1) |
Description
Minimize and protect administrator accounts.
Rationale
Why This Matters:
- Account admins can change authentication, governance, RBAC, and every connector, so minimizing their number reduces high-value targets
- Requiring SSO and 2FA on admin accounts hardens the most powerful credentials against phishing and reuse
- A small, documented admin set makes anomalous admin activity easy to spot and investigate
- Fewer standing admins limits the damage from a single compromised privileged account
Attack Prevented: Privileged-account takeover, configuration tampering, persistence, audit evasion
ClickOps Implementation
Step 1: Inventory Admins
- Review account admins
- Document admin access
- Identify unnecessary privileges
Step 2: Apply Restrictions
- Limit account admin to 2-3 users
- Require 2FA/SSO for admins
- Monitor admin activity
Code Implementation
Code Pack: Terraform
# Restricted administrator user group -- limit to 2-3 members
resource "harness_platform_usergroup" "platform_admins" {
identifier = "hth_platform_admins"
name = var.admin_user_group_name
description = "Restricted platform administrator group -- limit to 2-3 members (HTH Control 2.3)"
notification_configs {
type = "EMAIL"
send_email_to_all = true
}
}
# Resource group for account-level admin operations
resource "harness_platform_resource_group" "account_admin" {
identifier = "hth_account_admin"
name = "Account Administration"
description = "Resource group for account-level administrative operations (HTH Control 2.3)"
account_id = var.harness_account_id
included_scopes {
filter = "INCLUDING_CHILD_SCOPES"
}
resource_filter {
include_all_resources = true
}
}
# Bind the Account Admin role to the restricted admin group
resource "harness_platform_role_assignments" "admin_binding" {
identifier = "hth_admin_binding"
resource_group_identifier = harness_platform_resource_group.account_admin.id
role_identifier = "_account_admin"
principal {
identifier = harness_platform_usergroup.platform_admins.id
type = "USER_GROUP"
}
disabled = false
managed = false
}
3. Secret Management
3.1 Configure Secret Manager
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 3.11 |
| NIST 800-53 | SC-12 |
Description
Securely manage secrets for pipelines.
Rationale
Why This Matters:
- Centralizing secrets in a dedicated manager (Vault, a cloud KMS, or the built-in store) keeps credentials out of pipeline YAML and source control
- Referenced secrets are injected at runtime and masked in most log output, avoiding plaintext exposure to anyone who can read the pipeline — but masking is not absolute (see the callout below)
- External managers add rotation, versioning, and access auditing that hardcoded credentials lack
- Pipelines hold deploy keys and cloud credentials, so leaking one can compromise production infrastructure
Attack Prevented: Hardcoded-secret leakage, credential exposure in logs or VCS, supply-chain compromise via stolen deploy keys
ClickOps Implementation
Step 1: Configure Secret Manager
- Navigate to: Account Settings → Connectors → Secrets Managers
- Configure preferred secret manager:
- Harness Built-in
- HashiCorp Vault
- AWS Secrets Manager
- Azure Key Vault
- GCP Secret Manager
Step 2: Migrate Secrets
- Migrate existing secrets
- Reference secrets in pipelines
- Never hardcode credentials
Secret masking has a documented exception — Run step output variables. Harness states plainly: “Secrets in Run step output variables are exposed in logs.” Do not treat “secrets are masked in logs” as a blanket guarantee. Never promote a secret into a Run step output variable; pass it by secret reference at the point of use instead, and review existing pipelines for Run steps that emit credentials as outputs. Source: Security hardening for Harness CI.
Code Implementation
Code Pack: Terraform
# HashiCorp Vault connector for external secret management
resource "harness_platform_connector_vault" "vault" {
count = var.secret_manager_type == "vault" ? 1 : 0
identifier = "hth_vault_connector"
name = "HashiCorp Vault"
description = "External secret manager -- HashiCorp Vault (HTH Control 3.1)"
url = var.vault_url
access_type = "TOKEN"
default = true
read_only = false
renewal_interval_minutes = var.vault_renew_interval_minutes
secret_engine_name = var.vault_secret_engine
secret_engine_version = 2
use_vault_agent = false
use_aws_iam = false
use_k8s_auth = false
auth_token = var.vault_token
}
# AWS Secrets Manager connector
resource "harness_platform_connector_aws_secret_manager" "aws" {
count = var.secret_manager_type == "aws" ? 1 : 0
identifier = "hth_aws_secrets_connector"
name = "AWS Secrets Manager"
description = "External secret manager -- AWS Secrets Manager (HTH Control 3.1)"
default = true
secret_name_prefix = "harness/"
region = "us-east-1"
credentials {
inherit_from_delegate = true
}
}
# GCP Secret Manager connector
resource "harness_platform_connector_gcp_secret_manager" "gcp" {
count = var.secret_manager_type == "gcp" ? 1 : 0
identifier = "hth_gcp_secrets_connector"
name = "GCP Secret Manager"
description = "External secret manager -- GCP Secret Manager (HTH Control 3.1)"
default = true
is_default = true
credentials {
inherit_from_delegate = true
}
}
3.2 Configure Secret Access
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 3.11 |
| NIST 800-53 | SC-12 |
Description
Control access to secrets.
Rationale
Why This Matters:
- Scoping secrets to the narrowest level (project over account) ensures only the pipelines that need a credential can read it
- Restricting who can create and view secrets prevents quiet exfiltration of cloud and registry credentials
- Auditing secret access provides the trail needed to detect and investigate misuse
- Limiting account- and org-wide secrets stops one compromised project from reaching every other team’s credentials
Attack Prevented: Unauthorized secret access, credential exfiltration, lateral movement, insider misuse
ClickOps Implementation
Step 1: Scope Secrets
- Create secrets at appropriate level
- Use project-scoped secrets
- Limit organization/account secrets
Step 2: Configure Permissions
- Restrict secret creation
- Limit secret viewing
- Audit secret access
Code Implementation
Code Pack: Terraform
# Resource group scoped to secret management resources (L2+)
resource "harness_platform_resource_group" "secrets" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_secret_resources"
name = var.secret_resource_group_name
description = "Resource group limiting access to secret management resources (HTH Control 3.2)"
account_id = var.harness_account_id
included_scopes {
filter = "INCLUDING_CHILD_SCOPES"
}
resource_filter {
include_all_resources = false
resources {
resource_type = "SECRET"
}
resources {
resource_type = "CONNECTOR"
attribute_filter {
attribute_name = "category"
attribute_values = ["SECRET_MANAGER"]
}
}
}
}
# Role restricting secret operations to approved personnel (L2+)
resource "harness_platform_roles" "secret_operator" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_secret_operator"
name = var.secret_manager_role_name
description = "Role for managing secrets without broader platform access (HTH Control 3.2)"
permissions = [
"core_secret_view",
"core_secret_edit",
"core_secret_delete",
"core_connector_view"
]
allowed_scope_levels = ["account", "organization", "project"]
}
# User group for secret management personnel (L2+)
resource "harness_platform_usergroup" "secret_managers" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_secret_managers"
name = "Secret Managers"
description = "User group with scoped access to secret management resources (HTH Control 3.2)"
notification_configs {
type = "EMAIL"
send_email_to_all = true
}
}
# Bind secret operator role to the secret managers group (L2+)
resource "harness_platform_role_assignments" "secret_binding" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_secret_binding"
resource_group_identifier = harness_platform_resource_group.secrets[0].id
role_identifier = harness_platform_roles.secret_operator[0].id
principal {
identifier = harness_platform_usergroup.secret_managers[0].id
type = "USER_GROUP"
}
disabled = false
managed = false
}
3.3 Use OIDC for Cloud Connector Authentication
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 3.11, 6.5 |
| NIST 800-53 | IA-5, SC-12 |
Description
Authenticate Harness cloud connectors to cloud providers with OIDC federation instead of long-lived static keys, and use the OIDC token-generation plugins for pipeline steps that have no native connector.
Rationale
Why This Matters:
- Static cloud access keys stored as Harness secrets are bearer credentials that stay valid until someone remembers to rotate them, and they work from anywhere once leaked
- OIDC federation issues a short-lived, workload-scoped token per execution, so there is no standing cloud credential in the platform to steal
- Harness Cloud supports the OIDC connectivity mode for GCP connectors, letting the delegate-free build infrastructure authenticate without a service account key file
- Steps with no native connector can still go secretless — the GCP OIDC plugin generates a GCP access token from an OIDC token, and the Azure OIDC plugin does the same for Azure services
- A CI/CD platform holding permanent cloud keys is the highest-value single target in the supply chain; removing the keys removes the prize
Attack Prevented: Long-lived cloud credential theft, key exfiltration from CI logs or state, credential reuse outside the pipeline context, standing cloud access after platform compromise
ClickOps Implementation
Step 1: Configure the Cloud Connector for OIDC
- Navigate to: Account Settings → Connectors
- Create or edit the cloud provider connector (GCP, AWS, or Azure)
- Select the OIDC connectivity mode rather than key/secret authentication
- On the cloud provider side, register Harness as an OIDC identity provider and scope the trust policy to the specific account, organization, and project
Step 2: Cover Steps Without a Native Connector
- For steps that cannot consume a connector directly, add the vendor’s OIDC token-generation plugin (GCP OIDC plugin or Azure OIDC plugin) to exchange the OIDC token for a short-lived cloud access token
- Confirm the generated token is consumed in-step and never written to an output variable (see 3.1)
Step 3: Retire the Static Keys
- Inventory existing connectors still using static access keys
- Migrate each to OIDC, then delete the corresponding secret from the secret manager
- Revoke the retired keys at the cloud provider — deleting the Harness secret alone does not invalidate them
Reference: Security hardening for Harness CI
4. Pipeline Security
4.1 Configure Pipeline Governance
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 16.1 |
| NIST 800-53 | SA-15 |
Description
Implement pipeline governance controls.
Rationale
Why This Matters:
- OPA policies enforce baseline standards (approved steps, images, and connectors) so non-compliant or risky pipelines cannot run
- Mandatory approval gates for production insert human review between a change and its deployment
- Governance prevents an attacker or careless user from silently altering a pipeline to ship malicious artifacts
- Policy-as-code makes controls consistent, auditable, and resistant to one-off bypasses
Attack Prevented: Poisoned pipeline execution, unauthorized or unreviewed production deploys, policy bypass, supply-chain tampering
ClickOps Implementation
Step 1: Configure OPA Policies
- Navigate to: Account Settings → Governance
- Create OPA policies
- Enforce pipeline standards
Step 2: Configure Approval Gates
- Add manual approval stages
- Configure approval groups
- Require approvals for production
Step 3: Enforce the Gate in the SCM, Not Only in Harness
- Harness is explicit that “failed CI pipelines don’t inherently block PR merges” — Harness can send pipeline statuses to your PRs, but the merge block lives in your source control provider
- In the SCM, configure branch protection rules that require the Harness status check to pass before merge
- Add CODEOWNERS so security-relevant paths (pipeline definitions, connectors, policy files) require a designated reviewer
- Treat a green Harness pipeline as evidence, not as an enforced gate, until the SCM-side protections are in place
- Source: Security hardening for Harness CI
Code Implementation
Code Pack: Terraform
# Default OPA policy requiring approval stages in production pipelines (L2+)
resource "harness_platform_policy" "require_approval" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_require_prod_approval"
name = "Require Production Approval Stage"
rego = <<-REGO
package pipeline
deny[msg] {
input.pipeline.stages[_].stage.type == "Deployment"
input.pipeline.stages[_].stage.spec.environment.type == "Production"
not has_approval_stage
msg := "Production deployments must include an approval stage (HTH Control 4.1)"
}
has_approval_stage {
input.pipeline.stages[_].stage.type == "Approval"
}
REGO
}
# Policy set enforcing governance on pipeline save events (L2+)
resource "harness_platform_policyset" "pipeline_governance" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_pipeline_governance"
name = "HTH Pipeline Governance"
action = "onsave"
type = "pipeline"
enabled = true
policies {
identifier = harness_platform_policy.require_approval[0].id
severity = "error"
}
}
# Custom OPA governance policies from variable input (L2+)
resource "harness_platform_policy" "custom" {
for_each = var.profile_level >= 2 ? var.governance_policies : {}
identifier = each.key
name = each.value.name
rego = each.value.rego
}
# L3: Strict policy requiring signed artifacts in production pipelines
resource "harness_platform_policy" "require_artifact_verification" {
count = var.profile_level >= 3 ? 1 : 0
identifier = "hth_require_artifact_verification"
name = "Require Artifact Verification"
rego = <<-REGO
package pipeline
deny[msg] {
stage := input.pipeline.stages[_].stage
stage.type == "Deployment"
stage.spec.environment.type == "Production"
artifact := stage.spec.serviceConfig.serviceDefinition.spec.artifacts.primary
not artifact.spec.digest
msg := "Production deployments must reference artifacts by digest, not tag (HTH Control 4.1 L3)"
}
REGO
}
# L3: Policy set with strict enforcement including artifact verification
resource "harness_platform_policyset" "strict_governance" {
count = var.profile_level >= 3 ? 1 : 0
identifier = "hth_strict_pipeline_governance"
name = "HTH Strict Pipeline Governance (L3)"
action = "onrun"
type = "pipeline"
enabled = true
policies {
identifier = harness_platform_policy.require_approval[0].id
severity = "error"
}
policies {
identifier = harness_platform_policy.require_artifact_verification[0].id
severity = "error"
}
}
4.2 Configure Audit Trail
Profile Level: L1 (Crawl)
| Framework | Control |
|---|---|
| CIS Controls | 8.2 |
| NIST 800-53 | AU-2 |
Description
Enable and monitor audit logs.
Rationale
Why This Matters:
- Logging pipeline runs, configuration changes, permission edits, and secret access creates the evidence needed to detect and investigate incidents
- Without an audit trail, malicious deploys, privilege changes, and secret access go unnoticed and cannot be reconstructed
- Monitoring and retention support breach forensics and satisfy compliance evidence requirements
- A reliable audit record deters insiders and makes persistence and configuration drift visible
Attack Prevented: Undetected intrusion, insider misuse, repudiation, delayed breach detection
ClickOps Implementation
Step 1: Access Audit Trail
- Navigate to: Account Settings → Audit Trail
- Review logged events
- Configure retention
Step 2: Monitor Events
- Pipeline executions
- Configuration changes
- Permission modifications
- Secret access
Code Implementation
Code Pack: Terraform
# Note: Harness audit trail is enabled by default for all accounts.
# This configuration creates a service account and role for audit access,
# and optionally configures streaming destinations for log retention.
# Dedicated audit viewer role for compliance and security teams
resource "harness_platform_roles" "audit_viewer" {
identifier = "hth_audit_viewer"
name = "Audit Trail Viewer"
description = "Read-only role for accessing audit trail data (HTH Control 4.2)"
permissions = [
"core_audit_view"
]
allowed_scope_levels = ["account"]
}
# User group for audit/compliance personnel
resource "harness_platform_usergroup" "auditors" {
identifier = "hth_auditors"
name = "Audit & Compliance Team"
description = "User group with read-only access to audit trail (HTH Control 4.2)"
notification_configs {
type = "EMAIL"
send_email_to_all = true
}
}
# Resource group scoped to audit resources
resource "harness_platform_resource_group" "audit_resources" {
identifier = "hth_audit_resources"
name = "Audit Trail Resources"
description = "Resource group for audit trail access (HTH Control 4.2)"
account_id = var.harness_account_id
included_scopes {
filter = "INCLUDING_CHILD_SCOPES"
}
resource_filter {
include_all_resources = false
resources {
resource_type = "AUDIT"
}
}
}
# Bind audit viewer role to auditors group
resource "harness_platform_role_assignments" "audit_binding" {
identifier = "hth_audit_binding"
resource_group_identifier = harness_platform_resource_group.audit_resources.id
role_identifier = harness_platform_roles.audit_viewer.id
principal {
identifier = harness_platform_usergroup.auditors.id
type = "USER_GROUP"
}
disabled = false
managed = false
}
# Service account for automated audit log retrieval
resource "harness_platform_service_account" "audit_exporter" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_audit_exporter"
name = "Audit Log Exporter"
description = "Service account for automated audit log retrieval and streaming (HTH Control 4.2)"
email = "audit-exporter@harness.local"
account_id = var.harness_account_id
}
# API key for the audit exporter service account (L2+)
resource "harness_platform_apikey" "audit_exporter_key" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_audit_exporter_key"
name = "Audit Exporter API Key"
apikey_type = "SERVICE_ACCOUNT"
parent_id = harness_platform_service_account.audit_exporter[0].id
account_id = var.harness_account_id
}
# Token for the audit exporter API key (L2+)
resource "harness_platform_token" "audit_exporter_token" {
count = var.profile_level >= 2 ? 1 : 0
identifier = "hth_audit_exporter_token"
name = "Audit Exporter Token"
apikey_id = harness_platform_apikey.audit_exporter_key[0].id
apikey_type = "SERVICE_ACCOUNT"
parent_id = harness_platform_service_account.audit_exporter[0].id
account_id = var.harness_account_id
}
4.3 Generate and Enforce SBOM and SLSA Provenance
Profile Level: L2 (Walk)
| Framework | Control |
|---|---|
| CIS Controls | 2.1, 16.6 |
| NIST 800-53 | SA-15, SI-7 |
Description
Use the Harness Supply Chain Security (SCS) module to generate, store, and enforce SBOM and SLSA Provenance for pipeline-produced artifacts, so downstream consumers can verify what was built and by whom.
Rationale
Why This Matters:
- An SBOM makes the dependency set of every shipped artifact explicit, turning “are we exposed to this CVE?” from an investigation into a query
- SLSA Provenance cryptographically ties an artifact to the pipeline, source commit, and build parameters that produced it, so a substituted or side-loaded binary fails verification
- Enforcement is what makes provenance a control rather than metadata — generating an attestation nobody checks stops no attack
- Without provenance, a compromised build step can publish a tampered artifact under a trusted name and nothing downstream can tell the difference
- Harness CI can drive this through the SCS module, or via scripts in Run steps that generate SBOM and SLSA Provenance and upload the resulting artifacts
Attack Prevented: Supply-chain artifact substitution, tampered build output, undetected malicious dependency introduction, unverifiable release provenance
ClickOps Implementation
Step 1: Enable Supply Chain Security
- Enable the Supply Chain Security (SCS) module for the account
- Add SBOM generation to the pipelines that build release artifacts
- Confirm generated SBOMs are stored and retained, not just emitted to the build log
Step 2: Generate SLSA Provenance
- Add SLSA Provenance generation to release-producing pipelines
- Where a step has no SCS-native equivalent, generate SBOM and SLSA Provenance in a Run step script and upload the artifacts
Step 3: Enforce on Consumption
- Add a verification step that fails the pipeline when provenance is missing or does not match the expected builder and source
- Pair enforcement with the OPA governance policies in 4.1 so the requirement cannot be bypassed by editing a single pipeline
Reference: Security hardening for Harness CI
5. Compliance Quick Reference
SOC 2 Trust Services Criteria Mapping
| Control ID | Harness Control | Guide Section |
|---|---|---|
| CC6.1 | SSO/2FA | 1.1 |
| CC6.2 | RBAC | 2.1 |
| CC6.7 | Secret management | 3.1 |
| CC7.2 | Audit trail | 4.2 |
NIST 800-53 Rev 5 Mapping
| Control | Harness Control | Guide Section |
|---|---|---|
| IA-2 | SSO | 1.1 |
| IA-2(1) | 2FA | 1.2 |
| AC-6 | RBAC | 2.1 |
| SC-12 | Secret management | 3.1 |
| IA-5 | OIDC cloud connectors | 3.3 |
| AU-2 | Audit trail | 4.2 |
| SA-15 | SBOM and SLSA provenance | 4.3 |
Appendix A: References
Official Harness Documentation:
- Developer Hub
- Security Hardening for CI
- SAML SSO Configuration
- Two-Factor Authentication
- Role-Based Access Control (RBAC) in Harness
API & Developer Tools:
Compliance Frameworks:
- Harness publishes its SOC 2 / ISO attestation status through its Trust Center, which is a vendor assurance surface rather than a hardening source and is therefore not cited in this guide. Verify Harness’s current certification scope directly with the vendor under NDA or through your procurement process; this guide makes no claim about which certifications are currently held.
Security Incidents:
- No major public security incidents identified as of this revision (2026-08-08). Absence of a published incident is not evidence of absence — this reflects what was locatable in public sources on the review date.
Changelog
| Date | Version | Maturity | Changes | Author |
|---|---|---|---|---|
| 2026-08-08 | 0.2.0 | draft | Currency pass: add 3.3 (OIDC for cloud connectors) and 4.3 (SBOM/SLSA provenance via the SCS module); correct 1.2 to the actual “Enforce Two Factor Authentication” toggle with the admin-self-enrollment prerequisite and the one-or-both challenge rule; add a Tier 1 callout to 3.1 that secrets in Run step output variables ARE exposed in logs, contradicting the guide’s prior blanket masking claim; add a branch-protection/CODEOWNERS step to 4.1 because failed CI pipelines do not inherently block PR merges; repair the 404’d RBAC link and remove Trust Center / vendor security marketing links per the SOURCES.md bright line. Tier 2 not surveyed this pass (the CIS index was not checked); Tier 3/4 not surveyed. | Claude Code (Opus 4.8) |
| 2026-06-29 | 0.1.1 | draft | Add cheat-sheet Description and Rationale for all controls | Claude Code (Opus 4.8) |
| 2025-02-05 | 0.1.0 | draft | Initial guide with SSO, RBAC, and secret management | Claude Code (Opus 4.5) |
Contributing
Found an issue or want to improve this guide?
- Report outdated information: Open an issue with tag
content-outdated - Propose new controls: Open an issue with tag
new-control - Submit improvements: See Contributing Guide