Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f7b578a
feat(config): support env var overrides for AuthType, AccessId, Acces…
spbsoluble Jul 16, 2026
841842c
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 16, 2026
a977de5
docs: document Akeyless API endpoints called by the extension
spbsoluble Jul 16, 2026
6e69bab
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 16, 2026
e7e547d
docs: surface env var config in Requirements so it bubbles into README
spbsoluble Jul 16, 2026
b891cf3
docs: regenerate with fixed doctool (dedup Extension Mechanics, inclu…
spbsoluble Jul 16, 2026
d4d7f0e
Merge remote-tracking branch 'origin/release-1.1' into feature/env-va…
spbsoluble Jul 22, 2026
deddf75
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 22, 2026
44ded50
fix(tests): prevent env var override from masking bad-credentials test
spbsoluble Jul 22, 2026
f6db764
Merge remote-tracking branch 'origin/feature/env-var-credential-overr…
spbsoluble Jul 22, 2026
bc1bc0f
fix(docs): revert CI doc regeneration regression
spbsoluble Jul 22, 2026
f411c05
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 22, 2026
c373bf5
fix: whitespace-only env var overrides and add override audit log
spbsoluble Jul 22, 2026
a598f13
Merge remote-tracking branch 'origin/feature/env-var-credential-overr…
spbsoluble Jul 22, 2026
06c8db1
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 22, 2026
5c5089c
fix(docs): revert another CI doc regeneration regression
spbsoluble Jul 22, 2026
bf1f5b8
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Jul 22, 2026
4fa6b0f
fix: validate AKEYLESS_AUTH_TYPE override, trim env-var overrides, lo…
spbsoluble Aug 5, 2026
354cfad
fix: reject embedded line breaks in env overrides, log AccessId on au…
spbsoluble Aug 5, 2026
737af79
fix: reject control characters and Unicode line separators, not just …
spbsoluble Aug 5, 2026
1eb02ca
fix: reject Unicode format/bidi chars in overrides; stop logging raw …
spbsoluble Aug 5, 2026
9ad4156
fix: switch to a printable-ASCII allowlist, applied to both override …
spbsoluble Aug 5, 2026
43ee79c
fix: validate configured Url/AuthType/AccessId/AccessKey before Build…
spbsoluble Aug 5, 2026
8eabfec
fix: log before EnsurePrintableAscii rejects a non-ASCII value
spbsoluble Aug 5, 2026
20e63b4
refactor: extract ResolveAndValidate helper in InitClient
spbsoluble Aug 6, 2026
11dd843
refactor: drop vacuous single-case switch in InitClient
spbsoluble Aug 6, 2026
e755d96
fix: trim configured Url/AuthType/AccessId/AccessKey before validating
spbsoluble Aug 6, 2026
9925975
fix: null-safe AuthType/Url trim; warn on ambient credential env-var …
spbsoluble Aug 6, 2026
4c3e2b8
fix: whitespace-only configured Url falls back to default, not empty …
spbsoluble Aug 6, 2026
568aa1f
refactor: drop dead default-URL fallback in InitClient
spbsoluble Aug 6, 2026
97e8f74
refactor: fold embedded-line-break AccessId test into the control-cha…
spbsoluble Aug 6, 2026
a0f0980
docs: condense CHANGELOG entries for env var override feature
spbsoluble Aug 6, 2026
f0f6003
docs: auto-generate README and documentation [skip ci]
github-actions[bot] Aug 7, 2026
5b10ddc
Add get-secret-field debug scripts for manual Akeyless static_kv testing
spbsoluble Aug 26, 2026
b8f43b5
docs: split get-secret-field README into dedicated PowerShell/Bash se…
spbsoluble Aug 26, 2026
774e4b6
chore(docs): Update docs [skip ci]
spbsoluble Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# v1.1.0

## Features

- **Environment variable overrides for connection parameters** — `AuthType`, `AccessId`, and `AccessKey` can now be overridden at runtime via the `AKEYLESS_AUTH_TYPE`, `AKEYLESS_ACCESS_ID`, and `AKEYLESS_ACCESS_KEY` environment variables, matching the existing `AKEYLESS_API_URL` override. This lets deployments control Akeyless connection details at the infrastructure level instead of only via `manifest.json` or the Command portal.
- Environment variable overrides are trimmed of leading/trailing whitespace before use.
- Hardened validation and logging around connection parameters and authentication failures.

# v1.0.0

Initial release of the Akeyless PAM Provider for Keyfactor Command and Universal Orchestrator.
Expand Down
239 changes: 239 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ To install Akeyless PAM Provider, it is recommended you install [kfutil](https:/

#### Requirements
- Akeyless credentials w/ permission to access the secret(s) being used. See the [Akeyless documentation](https://docs.akeyless.io/reference/auth) for more information on how to configure the different types of auth.
- (Optional) `AKEYLESS_API_URL`, `AKEYLESS_AUTH_TYPE`, `AKEYLESS_ACCESS_ID`, and `AKEYLESS_ACCESS_KEY` environment variables can be set on the provider's host process to override the corresponding `manifest.json`/Command portal parameters at runtime. See [Configuration](docs/akeyless.md#configuration) for details and precedence.

#### Create PAM type in Keyfactor Command

Expand Down Expand Up @@ -313,6 +314,244 @@ When entering Secret fields, select the **Load From Keyfactor Secrets** tab, and
> [!NOTE]
> Additional information on Akeyless can be found in the [supplemental documentation](docs/akeyless.md).

#### Extension Mechanics

When configuring Akeyless for use as a PAM Provider with Keyfactor, you will need to ensure that your
instance is configured for API access using the desired auth method. This can be done by an Akeyless administrator.
For more details visit the vendor
docs [here](https://docs.akeyless.io/docs/access-and-authentication-methods).

Once API access is configured the credential *MUST* be granted access to view secret(s) you'll be using.

### Akeyless API Endpoints Used

The provider calls exactly two Akeyless REST API endpoints, both against the configured base URL (default `https://api.akeyless.io`, see the `Url` initialization parameter / `AKEYLESS_API_URL` environment variable above):

| Endpoint | Method | Called from | Purpose |
|---|---|---|---|
| [`/auth`](https://docs.akeyless.io/reference/auth) | `POST` | `AkeylessApiClient.Authenticate` (invoked once per `GetPassword` call, before secret retrieval) | Exchanges the configured `AccessId`/`AccessKey` for a short-lived auth token. |
| [`/get-secret-value`](https://docs.akeyless.io/reference/getsecretvalue) | `POST` | `AkeylessApiClient.GetSecretValuesAsync` (invoked once per `GetPassword` call, after authentication) | Retrieves the value of the secret named by the `SecretName` instance parameter, using the token from `/auth`. |

No other Akeyless API endpoints are called by this provider — it only ever authenticates and reads a single static secret value per credential lookup. It never creates, updates, deletes, or lists items in Akeyless.

### Granting an Auth Method Access to a Secret

In Akeyless, access is controlled through **Access Roles**. A role ties one or more auth methods to a set of permitted item paths. The steps below show how to grant an API Key auth method read access to a secret using the Akeyless console.

**1. Create an Access Role** (if one doesn't exist already)

Navigate to **Access Roles** → **New Role**, give it a name (e.g. `keyfactor-pam`), and save.

**2. Associate the Auth Method with the Role**

Open the role, go to the **Auth Methods** tab, and click **Associate**. Select the API Key auth method whose Access ID and Access Key you'll be configuring in Keyfactor.

**3. Add a secret access rule to the Role**

Still in the role, go to the **Access Rules** (or **Items**) tab and click **Add Rule**:

| Field | Value |
|---|---|
| Item path | The full path to your secret, e.g. `/my-org/my-app/db-password`. Wildcards are supported, e.g. `/my-org/my-app/*` |
| Access type | `read` |

Save the rule.

Once the rule is in place, the auth method can authenticate and retrieve any secret that matches the configured path. You can verify access using the Akeyless CLI:

```shell
akeyless auth --access-id <ACCESS_ID> --access-key <ACCESS_KEY>
akeyless get-secret-value --name /my-org/my-app/db-password --token <TOKEN>
```

### Granting an Auth Method Access to a Secret (CLI)

The full service account setup can be scripted using the Akeyless CLI. The `create-auth-method-api-key` command returns the Access ID and Access Key you'll need for the Keyfactor configuration.

```shell
# 1. Create the API Key auth method
# The response includes the Access ID and Access Key — save these.
akeyless create-auth-method-api-key --name /keyfactor/pam-auth-method

# 2. Create an access role
akeyless create-role --name keyfactor-pam

# 3. Associate the auth method with the role
akeyless assoc-role-auth-method \
--role-name keyfactor-pam \
--am-name /keyfactor/pam-auth-method

# 4. Grant the role read access to a secret path (wildcards supported)
akeyless set-role-rule \
--role-name keyfactor-pam \
--path "/my-org/my-app/*" \
--capability read
```

After adding and sharing a secret, you can use the secret's name (the "Secret name") to retrieve credentials from Akeyless as a PAM Provider.

### Running the PAM provider on Keyfactor Universal Orchestrator (UO)

When installing on the Universal Orchestrator (UO), the PAM provider is installed on and run from the UO host. Below is a sequence diagram
showing the flow of the PAM provider when it is run from the UO.

```mermaid
sequenceDiagram
KeyfactorCommand->>KeyfactorCommand: New job created.
UO->>KeyfactorCommand: Hello do you have any jobs for me?
KeyfactorCommand->>UO: Yes here's a job.
UO->>Akeyless: Hello here are my client credentials.
Akeyless->>UO: Here's your API token.
UO->>Akeyless: I need secret named `my_secret`, here's my API token.
Akeyless->>Akeyless: Check secret ACL.
Akeyless->>UO: This is allowed, here's the secret.
UO->>UO: Running job.
UO->>KeyfactorCommand: Job completed.
```

### Running the PAM provider on the Keyfactor Command Host

When installing the PAM provider on the Keyfactor Command Host, it is installed on and run from the Keyfactor Command host.
Below is a sequence diagram showing the flow of the PAM provider when it is run from the Keyfactor Command Host.

```mermaid
sequenceDiagram
KeyfactorCommand->>KeyfactorCommand: Creating a new job.
KeyfactorCommand->>Akeyless: Hello here are my credentials.
Akeyless->>KeyfactorCommand: Here's your API token.
KeyfactorCommand->>Akeyless: I need secret named `my_secret`, here's my API token.
Akeyless->>Akeyless: Check secret ACL.
Akeyless->>KeyfactorCommand: This is allowed, here's the secret.
UO->>KeyfactorCommand: Hello do you have any jobs for me?
KeyfactorCommand->>UO: Yes here's a job with these credentials I pulled from Akeyless.
UO->>UO: Running job.
UO->>KeyfactorCommand: Job completed.
```

## Configuration

Connection and authentication parameters can be set in two ways:

1. **`manifest.json`/Command portal parameters** — set via the `manifest.json` `InitializationInfo` block (Universal Orchestrator installs) or the corresponding fields in the Command portal PAM provider configuration (Command host installs). This is the standard way to configure the provider.
2. **Environment variables** — if set on the host process running the PAM provider (the Keyfactor Command server for local installs, or the Universal Orchestrator host for remote installs), these override whatever value is configured via `manifest.json` or the Command portal. This is useful when connection details need to be controlled at the infrastructure/deployment level rather than baked into provider configuration — for example, pointing different environments (dev/stage/prod) at different Akeyless instances or credentials without changing `manifest.json` or Command PAM provider settings.

| Environment Variable | Overrides | Falls Back To |
|---|---|---|
| `AKEYLESS_API_URL` | `Url` | configured `Url` initialization parameter, then default (`https://api.akeyless.io`) |
| `AKEYLESS_AUTH_TYPE` | `AuthType` | configured `AuthType` initialization parameter |
| `AKEYLESS_ACCESS_ID` | `AccessId` | configured `AccessId` initialization parameter |
| `AKEYLESS_ACCESS_KEY` | `AccessKey` | configured `AccessKey` initialization parameter |

Precedence for each: environment variable (if set to a non-empty, non-whitespace-only value) > configured initialization parameter > default (`Url` only). An environment variable that is unset, or explicitly set to an empty or whitespace-only string, is treated as "not overriding" and falls through to the configured value. An override value is trimmed of leading/trailing whitespace before use (a trailing newline is a common artifact of file-mounted/`envFrom` secret provisioning). The effective value of `Url`, `AuthType`, `AccessId`, and `AccessKey` — whether it came from an override or from Command's/`manifest.json`'s configuration — must be printable ASCII; any other character (an embedded newline, ANSI escape sequence, Unicode line separator, or a bidirectional-override/zero-width character such as U+202E) is rejected with an error rather than used as-is. When an override is active, the provider logs a **Warning** stating which environment variable is overriding (never the value), so an incident investigation can confirm whether the effective connection parameter matches Command's recorded configuration. An `AKEYLESS_AUTH_TYPE` override that does not match a supported auth type fails the request immediately rather than silently skipping authentication.

**Upgrading an existing installation:** if the provider's host process already has `AKEYLESS_AUTH_TYPE`, `AKEYLESS_ACCESS_ID`, or `AKEYLESS_ACCESS_KEY` set for an unrelated reason (for example, a co-located Akeyless CLI or another Akeyless SDK conventionally uses these same variable names), upgrading to a version of this provider that reads them will silently start authenticating with that ambient identity instead of the one recorded in `manifest.json`/Command — with no configuration change on Command's side. Check the host environment for these variable names before upgrading, and watch for the Warning-level "Environment variable override active" log line afterward.

## Supported Authentication Methods

### Access Key (API Key) Authentication
This method uses an Access Key and Access ID pair to authenticate to the Akeyless API. These credentials can be created in the Akeyless console.
For more information, see the [Akeyless documentation](https://tutorials.akeyless.io/docs/authentication-methods-and-api-key-authentication).

#### Example `manifest.json` configuration:

```json
{
"extensions": {
"Keyfactor.Platform.Extensions.IPAMProvider": {
"PAMProviders.Akeyless.PAMProvider": {
"assemblyPath": "akeyless-pam.dll",
"TypeFullName": "Keyfactor.Extensions.Pam.Akeyless.AkeylessPam"
}
}
},
"Keyfactor:PAMProviders:Akeyless-:InitializationInfo": {
"Url": "https://api.akeyless.io",
"AuthType": "access_key",
"AccessId": "<ACCESS_ID>",
"AccessKey": "<ACCESS_KEY>"
}
}
```

## Supported Secret Types
Below are the types of Akeyless secret that are supported by this provider.

### Static Secrets
For full details on static secrets, see the [Akeyless documentation](https://docs.akeyless.io/docs/secret-management/static-secrets).

| Secret Type | Description | Additional Fields |
|---------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
| `static_text` | A static secret whose value is returned as a plain string | N/A |
| `static_json` | A static secret containing JSON; a specific field can optionally be extracted | *Optional*: `StaticSecretFieldName`. Use this to parse a specific field value from a JSON secret, else the full JSON blob will be returned |
| `static_kv` | A static secret containing key-value pairs; a specific field is extracted by name | *Required*: `StaticSecretFieldName`. Use this to parse a specific field value from a key-value secret. For example `password`. |

---

#### `static_text`

A static secret whose entire value is a plain string. The value is returned as-is with no parsing.

**Example secret value in Akeyless:**
```
s3cr3tP@ssword!
```

**Example instance parameter configuration:**

| Parameter | Value |
|-----------|-------|
| `SecretName` | `/my-org/my-app/db-password` |
| `SecretType` | `static_text` |

---

#### `static_json`

A static secret whose value is a JSON object. The provider can return either the full JSON blob or a single extracted field.

- If `StaticSecretFieldName` is **omitted**, the full JSON string is returned.
- If `StaticSecretFieldName` is **provided**, only the value of that field is returned.

> **Note:** The Keyfactor Command portal may display `StaticSecretFieldName` as a required field. If you want the full JSON blob returned (no field extraction), enter a single space (` `) in the field — the provider treats whitespace-only values as empty.

**Example secret value in Akeyless:**
```json
{
"username": "db_user",
"password": "s3cr3tP@ssword!"
}
```

**Example instance parameter configuration (extract a single field):**

| Parameter | Value |
|-----------|-------|
| `SecretName` | `/my-org/my-app/db-credentials` |
| `SecretType` | `static_json` |
| `StaticSecretFieldName` | `password` |

---

#### `static_kv`

A static secret whose value is a set of key-value pairs, one per line in `key=value` format. A specific field must be named via `StaticSecretFieldName`.

**Example secret value in Akeyless:**
```
username=db_user
password=s3cr3tP@ssword!
host=db.example.com
```

**Example instance parameter configuration:**

| Parameter | Value |
|-----------|-------|
| `SecretName` | `/my-org/my-app/db-credentials` |
| `SecretType` | `static_kv` |
| `StaticSecretFieldName` | `password` |


## License

Apache License 2.0, see [LICENSE](LICENSE)
Expand Down
Loading