# Harness CI Plugin

<figure><img src="https://762006384-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJXOXEifAmpyvNVL1to%2Fuploads%2FNJsoKtG8Uf0Tm9M4MXcl%2Fkeeper%2Bharness.png?alt=media&#x26;token=0ac51e07-5eec-4bfc-bdd4-57657e94917c" alt=""><figcaption></figcaption></figure>

### Features

* Retrieve secrets from the Keeper Vault within the Harness CI pipeline
* Set secret credentials as build arguments in Harness CI pipeline
* Copy secure files from the Keeper Vault
* Plugin URL: <https://plugins.drone.io/plugins/keeper-plugin>

{% hint style="info" %}
For a complete list of Keeper Secrets Manager features see the [Overview](/en/keeperpam/secrets-manager/overview.md)
{% endhint %}

### Prerequisites

#### Keeper Secrets Manager Requirements

| Requirement       | Description                                                                                                                                                      |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| KSM Access        | Active Keeper Secrets Manager subscription ([Quick Start Guide](/en/keeperpam/secrets-manager/quick-start-guide.md))                                             |
| Add-on Enabled    | Secrets Manager add-on enabled for your Keeper account                                                                                                           |
| Role Membership   | Member of a Role with Secrets Manager enforcement policy enabled                                                                                                 |
| KSM Application   | A configured [Keeper Secrets Manager](/en/keeperpam/secrets-manager/about/terminology.md) Application with secrets shared to it                                  |
| KSM Configuration | An initialized [configuration](/en/keeperpam/secrets-manager/about/secrets-manager-configuration.md) (Base64 token or One-Time Access Token or JSON config file) |

#### Harness CI Requirements

* Active Harness [account](https://app.harness.io/auth/#/signin)
* A pipeline setup of [Harness CI](https://developer.harness.io/docs/continuous-integration/use-ci/prep-ci-pipeline-components/)
* Understanding of Harness CI [secrets management](https://developer.harness.io/docs/platform/secrets/add-use-text-secrets/)
* Install the [Keeper Plugin](https://plugins.drone.io/plugins/keeper-plugin)

### Configuration Types

The plugin supports three authentication methods. Choose the one that fits your security requirements:

| Type                         | Harness Secret Type | Use Case                     |
| ---------------------------- | ------------------- | ---------------------------- |
| One-Time Access Token (OTAT) | Text Secret         | Single-use, highest security |
| Base64 Token                 | Text Secret         | Reusable, standard security  |
| JSON Config File             | File Secret         | Full configuration, reusable |

### Setup Guide

#### Step 1: Configure Keeper Vault

1. Create a [Shared Folder](/en/enterprise-guide/sharing/folders.md) in Keeper Vault
2. [Create records](/en/enterprise-guide/creating-vault-records.md) containing your secrets inside the shared folder
3. Create a Secrets Manager [Application](/en/keeperpam/secrets-manager/quick-start-guide.md#create-a-secrets-manager-application)
4. Generate your preferred credential type:
   * One-Time Access Token ([OTAT](/en/keeperpam/secrets-manager/about/one-time-token.md))
   * Base64 [Token or JSON](/en/keeperpam/secrets-manager/about/secrets-manager-configuration.md#creating-a-secrets-manager-configuration) Config

#### Step 2: Create Harness CI Secret

Navigate to: **Project → Project Setup → Secrets → + New Secret**

**Option A: One-Time Access Token (Text Secret)**

1. Click **+ New Secret → Text**
2. Configure:
   * **Secret Name:** `keeper_otat_secret`
   * **Secret Value:** Paste the token (e.g., `US:xxxxx...`)
   * **Scope:** Project (recommended)
3. Click **Save**

{% hint style="info" %}
**Note:** One-time access tokens are single-use. Generate a new token for each pipeline run.
{% endhint %}

**Option B: Base64 Token (Text Secret)**

1. Click **+ New Secret → Text**
2. Configure:
   * **Secret Name:** `keeper_base64_secret`
   * **Secret Value:** Paste the base64-encoded token
   * **Scope:** Project (recommended)
3. Click **Save**

{% hint style="info" %}
**Note:** Ensure no line breaks or whitespace in the base64 string.
{% endhint %}

**Option C: JSON Config File (File Secret)**

1. Click **+ New Secret → File**
2. Configure:
   * **Secret Name:** `keeper_ksm_config_file`
   * **Upload File:** Select your KSM JSON config file
   * **Scope:** Project (recommended)
3. Click **Save**

Expected JSON structure:

```json
{
  "hostname": "keepersecurity.com",
  "clientId": "your-client-id",
  "privateKey": "your-private-key"
}
```

#### Step 3: Reference in Pipeline

Use the following syntax to reference your secret:

```yaml
settings:
  ksm_config: <+secrets.getValue("your_secret_identifier_name")>
```

### Quick Start

#### Pipeline Example

```yaml
pipeline:
  name: harness_keeper_plugin
  identifier: harness_keeper_plugin
  projectIdentifier: default_project
  orgIdentifier: default
  stages:
    - stage:
        name: HkpCI
        identifier: HkpCI
        type: CI
        spec:
          cloneCodebase: false
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
          execution:
            steps:
              - step:
                  type: Plugin
                  name: Fetch_Keeper_Secrets
                  identifier: Fetch_Keeper_Secrets
                  spec:
                    image: keeper/harness-plugin:latest
                    settings:
                      ksm_config: <+secrets.getValue("keeper_base64_secret")>
                      secrets: |
                        RECORD_UID/field/password > PASSWORD
                        RECORD_UID/field/login > USERNAME
              - step:
                  type: Run
                  name: Use_Secrets
                  identifier: Use_Secrets
                  spec:
                    image: alpine:3.20
                    shell: Sh
                    command: |
                      if [ -f /harness/secrets/USERNAME ] && [ -f /harness/secrets/PASSWORD ]; then
                        USERNAME=$(cat /harness/secrets/USERNAME)
                        PASSWORD=$(cat /harness/secrets/PASSWORD)
                        echo "Username: $USERNAME"
                        echo "Password retrieved successfully"
                      else
                        echo "Error: Secret files not found"
                        exit 1
                      fi
```

{% hint style="info" %}
**Important:** Replace `RECORD_UID` with the actual Record UID from your Keeper Vault.
{% endhint %}

Once the pipeline execution end then we will be able to see secrets printed in Print secrets step logs.

<figure><img src="https://762006384-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJXOXEifAmpyvNVL1to%2Fuploads%2FaggGT7TJmTqYDSEMMqYp%2Fimage.png?alt=media&#x26;token=f269170e-e348-4b1e-8e44-226fda09c6b1" alt=""><figcaption></figcaption></figure>

### Secrets Configuration

#### Keeper Notation Syntax

The `secrets` input uses Keeper Notation to specify which secrets to retrieve:

```yaml
settings:
  secrets: |
    RECORD_UID/field/password > PASSWORD
    RECORD_UID/field/login > USERNAME
    RECORD_UID/custom_field/apiKey > API_KEY
    RECORD_UID/file/certificate.crt > CERT_FILE
```

#### Notation Format

```
<record_uid>/<selector>/<field_name> > <destination_name>
```

<table><thead><tr><th width="290.93359375">Component</th><th>Description</th></tr></thead><tbody><tr><td><code>record_uid</code></td><td>The unique identifier of the Keeper record</td></tr><tr><td><code>selector</code></td><td>Type of data: <code>field</code>, <code>custom_field</code>, or <code>file</code></td></tr><tr><td><code>field_name</code></td><td>Name of the field or file to retrieve</td></tr><tr><td><code>destination_name</code></td><td>Output filename in <code>/harness/secrets/</code></td></tr></tbody></table>

#### Selector Types

<table><thead><tr><th width="174.6640625">Selector</th><th width="272.37890625">Description</th><th>Output Location</th></tr></thead><tbody><tr><td><code>field</code></td><td>Standard record fields (login, password, etc.)</td><td><code>/harness/secrets/&#x3C;destination></code></td></tr><tr><td><code>custom_field</code></td><td>Custom fields defined in the record</td><td><code>/harness/secrets/&#x3C;destination></code></td></tr><tr><td><code>file</code></td><td>File attachments</td><td><code>/harness/secrets/&#x3C;destination></code></td></tr></tbody></table>

#### Examples

```yaml
secrets: |
  # Standard fields
  abc123/field/password > DB_PASSWORD
  abc123/field/login > DB_USERNAME
  
  # Custom fields
  xyz789/custom_field/apiKey > API_KEY
  xyz789/custom_field/endpoint > API_ENDPOINT
  
  # File attachments
  def456/file/server.crt > SERVER_CERT
  def456/file/server.key > SERVER_KEY
```

{% hint style="info" %}
**Tip:** For complex values (arrays, key-value pairs), refer to the [Keeper Notation](/en/keeperpam/secrets-manager/about/keeper-notation.md) - Predicates documentation.
{% endhint %}

### Local Docker Runner Configuration

When using `runtime: type: docker` ([local runner](https://developer.harness.io/docs/continuous-integration/use-ci/set-up-build-infrastructure/define-a-docker-build-infrastructure/)) instead of Harness Cloud, you must configure [Shared Paths](https://developer.harness.io/docs/continuous-integration/use-ci/prep-ci-pipeline-components/#shared-paths) to share [data](https://developer.harness.io/docs/continuous-integration/use-ci/caching-ci-data/share-ci-data-across-steps-and-stages/#share-data-between-steps-in-a-stage) between pipeline on local:

```yaml
spec:
  cloneCodebase: false
  platform:
    os: Linux
    arch: Amd64
  runtime:
    type: docker
    spec: {}
  sharedPaths:
    - /harness
  execution:
    steps:
      # ... your steps
```

{% hint style="info" %}
**Why?** On Harness Cloud, `/harness` is automatically shared. On local Docker, you must explicitly configure `sharedPaths`.
{% endhint %}

### Security Best Practices

| Practice                   | Description                                                |
| -------------------------- | ---------------------------------------------------------- |
| Use One-Time Access Tokens | Generate fresh tokens for each pipeline run when possible  |
| Clean Up Secret Files      | Delete secret files after use in pipeline steps            |
| Appropriate Scope          | Use project-level scope unless broader access is required  |
| Limit Access               | Restrict who can edit pipelines (editors can read secrets) |

#### Secret Masking - Hiding Secrets from Logs

Harness CI automatically masks secrets in logs when printed to console. However:

* This only obscures output logs
* Pipeline editors can potentially extract secrets
* Always follow the principle of least privilege

### Troubleshooting

#### Common Issues

| Issue                     | Cause                                    | Solution                                                  |
| ------------------------- | ---------------------------------------- | --------------------------------------------------------- |
| `KSM config is required`  | Secret not found or expression incorrect | Verify secret name matches exactly (case-sensitive)       |
| `Missing required fields` | JSON config incomplete                   | Ensure JSON contains `hostname`, `clientId`, `privateKey` |
| Expression not resolved   | Secret scope mismatch                    | Ensure secret scope matches pipeline scope                |
| Token already used        | One-time tokens are single-use           | Generate a new OTAT for each run                          |

#### Issue: Pipeline Works on Cloud but Fails Locally

**Symptoms:** `Fetch_Keeper_Secrets` succeeds but next step fails reading `/harness/secrets/`

**Causes & Solutions:**

1. **Secret name mismatch**
   * Plugin writes: `> USERNAME` and `> PASSWORD`
   * Run step must check: `/harness/secrets/USERNAME` and `/harness/secrets/PASSWORD`
2. **Workspace not shared**
   * Add `sharedPaths: /harness` to your stage spec (see Local Docker Runner Configuration)

#### Debugging: Verify Plugin Output

Add a debug step after the plugin:

```yaml
- step:
    type: Run
    name: Debug_Secrets
    spec:
      image: alpine:3.20
      shell: Sh
      command: |
        echo "Contents of /harness/secrets:"
        ls -la /harness/secrets/ || echo "Directory not found"
```

If no files appear, check:

* Plugin configuration (secret names)
* Workspace sharing (for local runner)
* `ksm_config` setting (map to PLUGIN\_KSM\_CONFIG)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/en/keeperpam/secrets-manager/integrations/harness-ci-plugin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
