diff --git a/.github/workflows/keyfactor-bootstrap-workflow-v3.yml b/.github/workflows/keyfactor-bootstrap-workflow-v3.yml
index 042ba5a..b8c72b2 100644
--- a/.github/workflows/keyfactor-bootstrap-workflow-v3.yml
+++ b/.github/workflows/keyfactor-bootstrap-workflow-v3.yml
@@ -11,10 +11,9 @@ on:
jobs:
call-starter-workflow:
- uses: keyfactor/actions/.github/workflows/starter.yml@v3.1.2
+ uses: keyfactor/actions/.github/workflows/starter.yml@v5
secrets:
- token: ${{ secrets.V2BUILDTOKEN}}
- APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}}
- gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
- gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
- scan_token: ${{ secrets.SAST_TOKEN }}
+ token: ${{ secrets.V2BUILDTOKEN}} # REQUIRED
+ gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} # Only required for golang builds
+ gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} # Only required for golang builds
+ scan_token: ${{ secrets.SAST_TOKEN }} # REQUIRED
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5e356a..0be19fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+v.1.0.2
+
+- Added support for all auth methods currently supported by the Keyfactor AWS library.
+
v.1.0.1
- Added support for the AWS Code Signing Template
diff --git a/README.md b/README.md
index 647e905..59e9a7d 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
Support
-
+
·
Requirements
@@ -33,7 +33,6 @@
-
This integration allows for the Synchronization, Enrollment, and Revocation of certificates from the AWS ACM PCA. This is the AnyGateway REST version.
## Compatibility
@@ -41,7 +40,7 @@ This integration allows for the Synchronization, Enrollment, and Revocation of c
The AWSPCA CAPlugin AnyCA Gateway REST plugin is compatible with the Keyfactor AnyCA Gateway REST 25.4.0 and later.
## Support
-The AWSPCA CAPlugin AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket with your Keyfactor representative. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com.
+The AWSPCA CAPlugin AnyCA Gateway REST plugin is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com.
> To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab.
@@ -55,16 +54,16 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
2. On the server hosting the AnyCA Gateway REST, download and unzip the latest [AWSPCA CAPlugin AnyCA Gateway REST plugin](https://github.com/Keyfactor/aws-pca-caplugin/releases/latest) from GitHub.
-3. Copy the unzipped directory (usually called `net6.0` or `net8.0`) to the Extensions directory:
+3. Copy the unzipped directory (usually called `net8.0` or `net10.0`) to the Extensions directory:
```shell
Depending on your AnyCA Gateway REST version, copy the unzipped directory to one of the following locations:
- Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net6.0\Extensions
Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net8.0\Extensions
+ Program Files\Keyfactor\AnyCA Gateway\AnyGatewayREST\net10.0\Extensions
```
- > The directory containing the AWSPCA CAPlugin AnyCA Gateway REST plugin DLLs (`net6.0` or `net8.0`) can be named anything, as long as it is unique within the `Extensions` directory.
+ > The directory containing the AWSPCA CAPlugin AnyCA Gateway REST plugin DLLs (`net8.0` or `net10.0`) can be named anything, as long as it is unique within the `Extensions` directory.
4. Restart the AnyCA Gateway REST service.
@@ -82,23 +81,23 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
Populate using the configuration fields collected in the [requirements](#requirements) section.
- * **RoleArn** - Destination Role ARN to use for AWS auth. Supports the [profile] prefix when using Default SDK auth, e.g. [myprofile]arn:aws:iam::123456789012:role/MyRole.
- * **Region** - AWS Region (single region only, e.g. us-east-1).
- * **CAArn** - AWS ACM PCA Certificate Authority ARN. Example: arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- * **S3Bucket** - S3 bucket name used for PCA audit reports (inventory). The AWS identity in the Role context must have read/write permissions to this bucket.
- * **UseDefaultSdkAuth** - Use AWS SDK default credential inference (supports EC2 instance role, environment variables, shared credentials, etc.). If RoleArn is prefixed with [profile], that profile is prioritized.
- * **DefaultSdkAssumeRole** - If UseDefaultSdkAuth is true, setting this to true will perform AssumeRole into RoleArn using the inferred SDK credentials.
- * **UseOAuth** - Use OAuth OIDC authentication to obtain a token, then AssumeRole into RoleArn.
- * **OAuthScope** - OAuth scope to request.
- * **OAuthGrantType** - OAuth grant type to request (commonly client_credentials).
- * **OAuthUrl** - OAuth token endpoint URL.
- * **OAuthClientId** - OAuth client id (secret).
- * **OAuthClientSecret** - OAuth client secret (secret).
- * **UseIAM** - Use IAM user access key/secret to AssumeRole into RoleArn.
- * **IAMUserAccessKey** - IAM user access key (secret).
- * **IAMUserAccessSecret** - IAM user access secret (secret).
- * **ExternalId** - Optional sts:ExternalId to supply on AssumeRole calls.
- * **Enabled** - Flag to Enable or Disable gateway functionality. Disabling is primarily used to allow creation of the CA prior to configuration information being available.
+ * **RoleArn** - Destination Role ARN to use for AWS auth. Supports the [profile] prefix when using Default SDK auth, e.g. [myprofile]arn:aws:iam::123456789012:role/MyRole.
+ * **Region** - AWS Region (single region only, e.g. us-east-1).
+ * **CAArn** - AWS ACM PCA Certificate Authority ARN. Example: arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ * **S3Bucket** - S3 bucket name used for PCA audit reports (inventory). The AWS identity in the Role context must have read/write permissions to this bucket.
+ * **UseDefaultSdkAuth** - Use AWS SDK default credential inference (supports EC2 instance role, environment variables, shared credentials, etc.). If RoleArn is prefixed with [profile], that profile is prioritized.
+ * **DefaultSdkAssumeRole** - If UseDefaultSdkAuth is true, setting this to true will perform AssumeRole into RoleArn using the inferred SDK credentials.
+ * **UseOAuth** - Use OAuth OIDC authentication to obtain a token, then AssumeRole into RoleArn.
+ * **OAuthScope** - OAuth scope to request.
+ * **OAuthGrantType** - OAuth grant type to request (commonly client_credentials).
+ * **OAuthUrl** - OAuth token endpoint URL.
+ * **OAuthClientId** - OAuth client id (secret).
+ * **OAuthClientSecret** - OAuth client secret (secret).
+ * **UseIAM** - Use IAM user access key/secret to AssumeRole into RoleArn.
+ * **IAMUserAccessKey** - IAM user access key (secret).
+ * **IAMUserAccessSecret** - IAM user access secret (secret).
+ * **ExternalId** - Optional sts:ExternalId to supply on AssumeRole calls.
+ * **Enabled** - Flag to Enable or Disable gateway functionality. Disabling is primarily used to allow creation of the CA prior to configuration information being available.
2. Define [Certificate Profiles](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCP-Gateway.htm) and [Certificate Templates](https://software.keyfactor.com/Guides/AnyCAGatewayREST/Content/AnyCAGatewayREST/AddCA-Gateway.htm) for the Certificate Authority as required. One Certificate Profile must be defined per Certificate Template. It's recommended that each Certificate Profile be named after the Product ID. The AWSPCA CAPlugin plugin supports the following product IDs:
@@ -111,14 +110,12 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
4. In Keyfactor Command (v12.3+), for each imported Certificate Template, follow the [official documentation](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Configuring%20Template%20Options.htm) to define enrollment fields for each of the following parameters:
- * **LifetimeDays** - OPTIONAL: The number of days of validity to use when requesting certs. If not provided, default is 365
- * **SigningAlgorithm** - Required: AWS ACM PCA certificate signature algorithm to use when issuing certificates. Value is an AWS PCA SigningAlgorithm enum name (case-insensitive), e.g. SHA256WITHRSA, SHA384WITHRSA, SHA256WITHECDSA. If omitted, the plugin selects a default compatible with the CA key algorithm.
-
+ * **LifetimeDays** - OPTIONAL: The number of days of validity to use when requesting certs. If not provided, default is 365
+ * **SigningAlgorithm** - Required: AWS ACM PCA certificate signature algorithm to use when issuing certificates. Value is an AWS PCA SigningAlgorithm enum name (case-insensitive), e.g. SHA256WITHRSA, SHA384WITHRSA, SHA256WITHECDSA. If omitted, the plugin selects a default compatible with the CA key algorithm.
-## Authentication (Access Key + Secret)
+## Authentication
-The CAPlugin currently supports **one** authentication method: **AWS Access Key ID + Secret Access Key**.
-**OAuth** and **Default SDK authentication** will be enabled in later updates. There is functionality present via the **Keyfactor AWS Authentication** library, but these alternate methods are currently ***untested***.
+The CAPlugin authenticates to AWS through the **Keyfactor AWS Authentication** library, which supports several methods. All of the methods below are supported and validated against ACM PCA. Choose **one** method per CA connection by setting the corresponding toggles/fields in **CAConnection**.
### Known Issues
@@ -127,83 +124,116 @@ Latest version of Command 25.4 may run into issues, investigation into compatibi
### What you need ready
-Before configuring the CAPlugin, have the following prepared:
+> **Key concept — the *destination* identity.** Whichever identity the plugin ends up operating as — the assumed `RoleArn`, or the *originating* identity when no Assume Role is performed — is the **destination**, and it is what must hold the ACM PCA + S3 audit-report permissions (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)). For every Assume-Role method the **originating** identity additionally needs `sts:AssumeRole` on the target role, and the target role's **trust policy** must allow that originating principal (see [Example trust policies](#example-trust-policies)).
+
+### Common requirements (all methods)
+
+- **Region** — the AWS region for service endpoints; must match your **ACM Private CA** region (e.g. `us-east-2`).
+- **CAArn** — the Certificate Authority ARN, e.g. `arn:aws:acm-pca:::certificate-authority/`.
+- **S3Bucket** — bucket name (not ARN) for ACM PCA audit reports, e.g. `keyfactor-acmpca-audit-reports`. The destination identity needs `s3:ListBucket`/`s3:GetBucketLocation` on the bucket and `s3:GetObject`/`s3:PutObject` on `/*`, and ACM PCA itself must be allowed to write to it (see [Example policy for bucket](#example-policy-for-bucket)).
+- **PCA Root Cert** — download the PCA root certificate from AWS in `.pem` format to upload under **Gateway Registration**.
+- **Scan interval** — ACM PCA limits audit-report generation to roughly **once per 30 minutes** per CA. Set the CA's `ServiceSettings` full-scan interval to **≥ 30 minutes** to avoid throttling.
+
+### Method configuration
+
+For every method, also set the common `Region`, `CAArn`, and `S3Bucket`, and upload the Root CA under **Gateway Registration**. Set all auth toggles you are **not** using to `false`.
+
+#### IAM User (Access Key + Secret)
+- `UseIAM` = `true`
+- `IAMUserAccessKey` (e.g. `AKIAIOSFODNN7EXAMPLE`), `IAMUserAccessSecret` (e.g. `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`)
+- `RoleArn` — the destination role to assume (e.g. `arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`)
+
+**AWS setup:** the IAM user needs `sts:AssumeRole` on `RoleArn`; `RoleArn`'s trust policy must allow the IAM user (see [Assume-Role trust](#example-trust-policies)); `RoleArn` holds the [PCA + S3 permissions](#example-iam-policies-for-the-destination-identity).
+
+#### IAM User + ExternalId
+As **IAM User**, plus set `ExternalId`. The target role's trust policy must include a matching `sts:ExternalId` condition (see [Assume-Role with ExternalId](#example-trust-policies)). Use this when the role owner requires a shared secret to prevent the confused-deputy problem.
+
+#### Default SDK (no assume)
+- `UseDefaultSdkAuth` = `true`, `DefaultSdkAssumeRole` = `false` (`RoleArn` is ignored)
+
+The Gateway host's **ambient AWS identity** is the destination and must hold the PCA + S3 permissions directly. The AWS SDK resolves this from, in order: `AWS_*` environment variables, the shared credentials file `[default]` profile, or an EC2 instance role. On a Windows AnyCA Gateway host the service runs as `NETWORK SERVICE`, so provide credentials via the machine-level env var `AWS_SHARED_CREDENTIALS_FILE` (pointing at a credentials file readable by the service) or the service account's `%USERPROFILE%\.aws\credentials`.
+
+#### Default SDK + Assume Role
+- `UseDefaultSdkAuth` = `true`, `DefaultSdkAssumeRole` = `true`, `RoleArn` = destination role
+
+The ambient identity (resolved as above) is the **originating** account and performs `sts:AssumeRole` into `RoleArn`. The ambient identity needs `sts:AssumeRole`; `RoleArn`'s trust allows it and holds the PCA + S3 permissions.
+
+#### Credential Profile (± Assume Role)
+Prefix the `RoleArn` with a `[profile-name]`, e.g. `[myprofile]arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`, and set `UseDefaultSdkAuth` = `true`. The named profile must exist in the shared AWS credentials file **on the Gateway host** (same location rules as Default SDK above).
+
+- **No assume** (`DefaultSdkAssumeRole` = `false`): the profile identity is the destination and needs the PCA + S3 permissions directly.
+- **With assume** (`DefaultSdkAssumeRole` = `true`): the profile is the originating identity and assumes `RoleArn` (which holds the permissions and trusts the profile identity).
+
+#### OAuth (OIDC federation)
+- `UseOAuth` = `true`
+- `OAuthClientId`, `OAuthClientSecret`, `OAuthUrl` (the token endpoint), `OAuthScope`, `OAuthGrantType` (`client_credentials`)
+- `RoleArn` = destination role (federated web-identity role)
-#### 1) IAMUserAccessKey and IAMUserAccessSecret
-- **Access Key ID** (example format: `AKIAIOSFODNN7EXAMPLE`)
-- **Secret Access Key** (example format: `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`)
+The plugin requests an OAuth token (`client_credentials` grant, client id/secret sent as an HTTP **Basic** `Authorization` header) and calls `sts:AssumeRoleWithWebIdentity` on `RoleArn`.
-#### 2) A target IAM Role the Gateway will run as (recommended)
-Example:
-- `arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`
+**AWS setup:** create an IAM **OIDC identity provider** for the token issuer, and set `RoleArn`'s trust policy to allow `sts:AssumeRoleWithWebIdentity` for that provider with the appropriate `aud` (and optionally `sub`) conditions (see [OAuth/web-identity trust](#example-trust-policies)).
-**Role expectations:**
-- The role must have permissions for:
- - **ACM PCA operations** (Issue/Get/Revoke/Describe + CA certificate chain retrieval)
- - **Audit report creation & status polling** (Create/Describe audit reports)
- - **S3 bucket access** to read/write audit report objects
+> OAuth notes: the OIDC provider must accept the client id/secret as a **Basic auth header** (not POST body); **DPoP must be disabled** on the app; and the token lifetime must be **≤ 12 hours** — STS `AssumeRoleWithWebIdentity` caps `DurationSeconds` at 43200, and the plugin derives the session duration from the token's `expires_in`.
-#### 3) Permissions on the assumed role
-The assumed role must have permissions for the AWS services the Gateway needs. This typically includes:
-- `acm-pca:*` actions required for enrollment + revocation workflows
-- Audit report actions (`acm-pca:CreateCertificateAuthorityAuditReport`, `acm-pca:DescribeCertificateAuthorityAuditReport`)
-- S3 bucket and object access for the audit report destination bucket
+### Setting up AWS Authentication (Examples)
-**See the example IAM policies below in this README section**
+> [!NOTE]
+> Several different options are offered for authenticating with AWS.
+> Documentation for how these options work is maintained in the [aws-auth-library](https://github.com/Keyfactor/aws-auth-library) repository.
-#### 4) Region
-Know the **AWS region** the connector should use (for service endpoints), e.g.:
-- `us-east-1`
+The following examples show potential configurations for Roles in AWS with different selected authentication methods. Your configuration steps may differ depending on the specific requirements of your use case. In every case the **destination identity** must hold the ACM Private CA + S3 audit-report permissions (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)), and every Assume-Role method additionally requires the *originating* identity to have `sts:AssumeRole` and the target role's **trust policy** to allow that principal (see [Example trust policies](#example-trust-policies)).
-> Region must match the region of your **ACM Private CA**.
+
+Host instance credentials using Default SDK and Assume Role
-#### 5) CA ARN
-Have the **Certificate Authority ARN** for the PCA you want to integrate with.
+Select the `Use Default SDK Auth` option (`UseDefaultSdkAuth=true`) to have the plugin load the Gateway host's ambient AWS credentials. If the Gateway runs on an EC2 instance, this is the IAM Role assigned to the instance; on a non-EC2 Windows host it is whatever the service account resolves from `AWS_SHARED_CREDENTIALS_FILE` or `%USERPROFILE%\.aws\credentials` (the AnyCA Gateway service runs as `NETWORK SERVICE`).
-Example format:
-- `arn:aws:acm-pca:::certificate-authority/`
+If that ambient identity is itself the Destination account identity to use with ACM Private CA, no additional Role needs to be configured. If it is only to be used initially and a separate `RoleArn` is designated as the Destination account, also select `Assume new Role using Default SDK Auth` (`DefaultSdkAssumeRole=true`).
-Example:
-- `arn:aws:acm-pca:us-east-2:123456789012:certificate-authority/11111111-2222-3333-4444-555555555555`
+**AWS Setup**
+_Note: to use instance credentials, the CAPlugin's Gateway host must be running inside an EC2 instance._
+1. Assign or note the existing IAM Role assigned to the EC2 instance (or the credentials configured for the service account).
+2. If Assume Role is used, ensure a [Trust Relationship](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/edit_trust.html) is set up on the Destination role that allows the ambient identity to assume it.
+3. Verify the permissions match the requirements for accessing ACM Private CA (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)).
-#### 6) S3 Bucket
-Choose an S3 bucket to store / retrieve ACM PCA audit reports.
+
-You should have:
-- **Bucket name** (example: `keyfactor-acmpca-audit-reports`, not the full bucket ARN!)
+
+OAuth OIDC Identity Provider (Okta example)
+Select the `Use OAuth` option (`UseOAuth=true`) for a CA connection to use an OAuth Identity Provider, and supply `OAuthClientId`, `OAuthClientSecret`, `OAuthUrl` (token endpoint), `OAuthScope`, and `OAuthGrantType` (`client_credentials`).
-> The role needs `s3:ListBucket` / `s3:GetBucketLocation` at the bucket ARN, and `s3:GetObject` / `s3:PutObject` on the object ARN pattern.
+**AWS Setup**
+1. A 3rd party [Identity Provider](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) — an IAM **OIDC identity provider** for the token issuer — needs to be set up in AWS.
+2. An [AWS Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html) (the `RoleArn`) needs to be created for use with your Identity Provider.
+3. Ensure the [Trust Relationship](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/edit_trust.html) is set up for that role with the Identity Provider, allowing `sts:AssumeRoleWithWebIdentity` with the appropriate `aud` (and optionally `sub`) conditions (see [OAuth / web-identity trust](#example-trust-policies)).
+4. Verify the permissions match the requirements for accessing ACM Private CA.
-#### 7) PCA Root Cert
-Download the **PCA root certificate** from AWS and have it ready to import into the Gateway **in `.pem` format**.
+**OKTA Setup**
+1. Ensure your Authorization Server is set up in Okta.
+2. Ensure the appropriate scopes are set up in Okta.
+3. Set up an Okta App (client-credentials / API Services type). The app must send the client id/secret as a **Basic auth header** (not POST body), have **DPoP disabled**, and issue tokens with a lifetime **≤ 12 hours** (STS caps `DurationSeconds` at 43200).
-### Enabling all this in the Gateway Configuration Portal
+
-#### 1) Register the Gateway CA and upload the Root CA certificate
-1. Navigate to **Gateway Registration**.
-2. Upload the **Root CA Certificate** you downloaded earlier (PEM).
+
+IAM User credentials to Assume Role
-#### 2) Configure the CA connection settings
-1. Navigate to **CAConnection**.
-2. Populate:
- - `RoleArn` (example: `arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`)
- - `Region` (example: `us-east-2`)
- - `CAArn` (example: `arn:aws:acm-pca:us-east-2:123456789012:certificate-authority/11111111-2222-3333-4444-555555555555`)
- - `S3Bucket` (example: `keyfactor-acmpca-audit-reports`)
- - `IAMUserAccessKey` (example: `AKIA...`)
- - `IAMUserAccessSecret` (example: `wJalrXU...`)
+Select the `Use IAM` option (`UseIAM=true`) for a CA connection to use an IAM User credential, supplying `IAMUserAccessKey` and `IAMUserAccessSecret`.
-3. Set these auth toggles:
- - `UseDefaultSdkAuth` = `false`
- - `UseOAuth` = `false`
- - `UseIAM` = `true`
+**AWS Setup**
+1. An [AWS Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html) to Assume with your IAM User needs to be created (set as `RoleArn`).
+2. Ensure a [Trust Relationship](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/edit_trust.html) is set up for that role that allows the IAM User (see [Assume-Role trust](#example-trust-policies)). If using the ExternalId modifier, add a matching `sts:ExternalId` condition.
+3. AWS does not support programmatic access for AWS SSO accounts. The account used here must be a standard AWS IAM User with an Access Key credential type.
+4. Verify the permissions match the requirements for accessing ACM Private CA (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)).
+
+
---
-### Example IAM policies for the assumed role
+### Example IAM policies for the destination identity
-The following examples are intended as **copy/adapt templates**.
+These permissions belong on the **destination identity** (the assumed `RoleArn`, or the ambient/profile identity when no Assume Role is performed). The following examples are intended as **copy/adapt templates**.
#### Example 1: Minimal PCA issuance/retrieval/revocation
@@ -278,6 +308,70 @@ The following examples are intended as **copy/adapt templates**.
```
---
+### Example trust policies
+
+Every Assume-Role method requires a **trust policy** on the destination role that allows the originating principal to assume it. (Default SDK *no-assume* and Credential Profile *no-assume* need no trust policy — the identity is used directly.)
+
+#### Assume Role — IAM User, Default SDK + Assume, or Credential Profile + Assume
+
+`<>` is the IAM user ARN (IAM User method) or the ambient/profile identity ARN (Default SDK / Credential Profile + Assume).
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": { "AWS": "<>" },
+ "Action": "sts:AssumeRole"
+ }
+ ]
+}
+```
+
+#### Assume Role with ExternalId
+
+Add a matching `sts:ExternalId` condition; the CAConnection `ExternalId` value must equal it.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": { "AWS": "<>" },
+ "Action": "sts:AssumeRole",
+ "Condition": { "StringEquals": { "sts:ExternalId": "<>" } }
+ }
+ ]
+}
+```
+
+#### OAuth — web-identity trust
+
+`RoleArn` for the OAuth method must trust the OIDC provider you registered for your token issuer. Condition on the token `aud` (audience) and optionally `sub` (for `client_credentials`, `sub` is typically the client id).
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": { "Federated": "arn:aws:iam:::oidc-provider//" },
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "/:aud": "<>",
+ "/:sub": "<>"
+ }
+ }
+ }
+ ]
+}
+```
+
+---
+
### Example policy for bucket
```json
{
@@ -350,11 +444,10 @@ When `SigningAlgorithm` is omitted, the plugin selects:
- SM2 -> `SM3WITHSM2`
- ML-DSA -> exact-match (`ML_DSA_44/65/87`)
-
## License
Apache License 2.0, see [LICENSE](LICENSE).
## Related Integrations
-See all [Keyfactor Any CA Gateways (REST)](https://github.com/orgs/Keyfactor/repositories?q=anycagateway).
\ No newline at end of file
+See all [Keyfactor Any CA Gateways (REST)](https://github.com/orgs/Keyfactor/repositories?q=anycagateway).
diff --git a/aws-pca-caplugin/AWSPCACAPlugin.cs b/aws-pca-caplugin/AWSPCACAPlugin.cs
index 5de07a7..ae21afa 100644
--- a/aws-pca-caplugin/AWSPCACAPlugin.cs
+++ b/aws-pca-caplugin/AWSPCACAPlugin.cs
@@ -95,8 +95,8 @@ public async Task Synchronize(
CancellationToken cancelToken)
{
Logger.MethodEntry();
- Logger.LogTrace(
- $"Synchronize started. fullSync={fullSync}, lastSync={lastSync?.ToUniversalTime().ToString("O") ?? "null"}");
+ Logger.LogInformation(
+ $"AWS PCA Synchronize started. fullSync={fullSync}, lastSync={lastSync?.ToUniversalTime().ToString("O") ?? "(none)"}");
try
{
@@ -105,7 +105,8 @@ public async Task Synchronize(
throw new Exception($"AWS audit report failed: {report.RegistrationError.Description}");
var items = report?.Result ?? new List();
- Logger.LogDebug($"Sync found {items.Count} audit records.");
+ Logger.LogInformation($"AWS PCA Synchronize: retrieved {items.Count} audit record(s) from the CA.");
+ int emitted = 0;
foreach (var audit in items)
{
@@ -252,6 +253,7 @@ public async Task Synchronize(
Status = (int)EndEntityStatus.INPROCESS,
ProductID = certResp.CertificateType
}, cancelToken);
+ emitted++;
continue;
}
@@ -294,9 +296,12 @@ public async Task Synchronize(
// Emit to buffer as AnyGateway expects.
blockingBuffer.Add(finalsubmit, cancelToken);
+ emitted++;
}
blockingBuffer.CompleteAdding();
+ Logger.LogInformation(
+ $"AWS PCA Synchronize complete: scanned {items.Count} audit record(s), emitted {emitted} certificate(s) to Keyfactor (added/updated/status-changed). fullSync={fullSync}.");
Logger.MethodExit();
}
catch (Exception e)
@@ -341,14 +346,14 @@ public async Task Revoke(string caRequestID, string hexSerialNumber, uint r
return (int)EndEntityStatus.FAILED;
}
- // Prefer the normalized numeric status from the client response if present.
- if (resp != null && resp.Status > 0)
- return resp.Status;
-
- // Fallback if Status wasn't implemented on response
- return resp?.RevokeSuccess == true
- ? (int)EndEntityStatus.REVOKED
- : (int)EndEntityStatus.FAILED;
+ // Prefer the normalized numeric status from the client response if present;
+ // otherwise fall back to RevokeSuccess.
+ var revokedStatus = (resp != null && resp.Status > 0)
+ ? resp.Status
+ : (resp?.RevokeSuccess == true ? (int)EndEntityStatus.REVOKED : (int)EndEntityStatus.FAILED);
+ Logger.LogInformation(
+ $"AWS PCA Revoke completed: serial={serial}, reason={awsReason}, resultStatus={revokedStatus}.");
+ return revokedStatus;
}
catch (Exception ex)
{
@@ -440,59 +445,35 @@ public async Task Enroll(
string priorRequestId;
try
{
- return await IssueAndFetchAsync(
- csr,
- productInfo.ProductID,
- days,
- signingAlgorithm,
- "Certificate Issued")
+ priorRequestId = await _certificateDataReader
+ .GetRequestIDBySerialNumber(priorSn)
.ConfigureAwait(false);
}
-
- case EnrollmentType.RenewOrReissue:
+ catch (Exception ex)
{
- if (productInfo.ProductParameters == null ||
- !TryGetProductParam(productInfo.ProductParameters, "PriorCertSN", out var priorSn) ||
- string.IsNullOrWhiteSpace(priorSn))
- return new EnrollmentResult
- {
- Status = (int)EndEntityStatus.FAILED,
- StatusMessage =
- "Renew/Reissue requires ProductParameters['PriorCertSN'] (hex serial number)."
- };
-
- string priorRequestId;
- try
- {
- priorRequestId = await _certificateDataReader
- .GetRequestIDBySerialNumber(priorSn)
- .ConfigureAwait(false);
- }
- catch (Exception ex)
+ return new EnrollmentResult
{
- return new EnrollmentResult
- {
- Status = (int)EndEntityStatus.FAILED,
- StatusMessage = $"Could not resolve PriorCertSN to request id: {ex.Message}"
- };
- }
+ Status = (int)EndEntityStatus.FAILED,
+ StatusMessage = $"Could not resolve PriorCertSN to request id: {ex.Message}"
+ };
+ }
- var expiration = _certificateDataReader.GetExpirationDateByRequestId(priorRequestId);
- var isRenewal = expiration.HasValue && expiration.Value.ToUniversalTime() <= DateTime.UtcNow;
+ var expiration = _certificateDataReader.GetExpirationDateByRequestId(priorRequestId);
+ var isRenewal = expiration.HasValue && expiration.Value.ToUniversalTime() <= DateTime.UtcNow;
- var msg = isRenewal ? "Certificate Renewed" : "Certificate Reissued";
- var token = BuildIdempotencyToken(isRenewal ? "renew" : "reissue", priorRequestId, csr);
+ var msg = isRenewal ? "Certificate Renewed" : "Certificate Reissued";
+ var token = BuildIdempotencyToken(isRenewal ? "renew" : "reissue", priorRequestId, csr);
- // Still "IssueCertificate" under the hood; PCA doesn't have first-class renew/reissue.
- return await IssueAndFetchAsync(
- csr,
- productInfo.ProductID,
- days,
- msg,
- // Optional: stable-ish idempotency (helps avoid duplicates if caller retries quickly)
- token)
- .ConfigureAwait(false);
- }
+ // Still "IssueCertificate" under the hood; PCA doesn't have first-class renew/reissue.
+ return await IssueAndFetchAsync(
+ csr,
+ productInfo.ProductID,
+ days,
+ msg,
+ // Optional: stable-ish idempotency (helps avoid duplicates if caller retries quickly)
+ token)
+ .ConfigureAwait(false);
+ }
default:
return new EnrollmentResult
@@ -529,8 +510,9 @@ public async Task Ping()
}
try
{
- Logger.LogInformation("Ping request received");
+ Logger.LogInformation("AWS PCA Ping request received; contacting AWS...");
await AwsClient.PingAsync().ConfigureAwait(false);
+ Logger.LogInformation("AWS PCA Ping succeeded (AWS reachable).");
}
catch (Exception e)
{
@@ -769,16 +751,23 @@ private async Task IssueAndFetchAsync(
IdempotencyToken = idempotencyToken ?? Guid.NewGuid().ToString("N")
};
+ Logger.LogInformation(
+ $"AWS PCA IssueCertificate requested: productId={productId}, validityDays={validityDays}, signingAlgorithm={signingAlgorithm ?? "(auto)"}.");
+
var issueResp = await AwsClient.SubmitIssueCertificateAsync(issueReq).ConfigureAwait(false);
if (issueResp?.RegistrationError != null ||
issueResp?.Result == null ||
string.IsNullOrWhiteSpace(issueResp.Result.CertificateArn))
+ {
+ var failMsg = issueResp?.RegistrationError?.Description ?? "AWS PCA IssueCertificate failed.";
+ Logger.LogError($"AWS PCA IssueCertificate failed (productId={productId}): {failMsg}");
return new EnrollmentResult
{
Status = (int)EndEntityStatus.FAILED,
- StatusMessage = issueResp?.RegistrationError?.Description ?? "AWS PCA IssueCertificate failed."
+ StatusMessage = failMsg
};
+ }
var certArn = issueResp.Result.CertificateArn;
var caRequestId = issueResp.Result.CertificateId ?? SafeRequestIdFromArn(certArn) ?? certArn;
@@ -811,6 +800,8 @@ private async Task IssueAndFetchAsync(
StatusMessage = "AWS PCA returned an empty certificate."
};
+ Logger.LogInformation(
+ $"AWS PCA enrollment succeeded ({statusMessageOnSuccess}): productId={productId}, caRequestId={caRequestId}, certificateArn={certArn}, validityDays={validityDays}, status={certResp.Status}.");
return new EnrollmentResult
{
CARequestID = caRequestId,
diff --git a/aws-pca-caplugin/AWSPCACAPlugin.csproj b/aws-pca-caplugin/AWSPCACAPlugin.csproj
index a53434c..b5796b4 100644
--- a/aws-pca-caplugin/AWSPCACAPlugin.csproj
+++ b/aws-pca-caplugin/AWSPCACAPlugin.csproj
@@ -12,13 +12,19 @@
-
-
+
+
+
-
-
Always
@@ -26,17 +32,15 @@
-
-
+
-
-
+
+
-
-
+
\ No newline at end of file
diff --git a/aws-pca-caplugin/AWSPCACAPlugin.csproj.user.example b/aws-pca-caplugin/AWSPCACAPlugin.csproj.user.example
new file mode 100644
index 0000000..188e87b
--- /dev/null
+++ b/aws-pca-caplugin/AWSPCACAPlugin.csproj.user.example
@@ -0,0 +1,22 @@
+
+
+
+
+ C:\path\to\your\push-script.ps1
+
+
diff --git a/aws-pca-caplugin/Client/ACMPCAClient.cs b/aws-pca-caplugin/Client/ACMPCAClient.cs
index 8f350d6..ac26eab 100644
--- a/aws-pca-caplugin/Client/ACMPCAClient.cs
+++ b/aws-pca-caplugin/Client/ACMPCAClient.cs
@@ -453,14 +453,37 @@ private async Task DownloadS3ObjectAsStringAsync(
{
var s3 = await GetOrCreateS3ClientAsync(cancellationToken).ConfigureAwait(false);
- using var response = await s3.GetObjectAsync(new GetObjectRequest
+ // ACM PCA audit-report generation is asynchronous: CreateCertificateAuthorityAuditReport
+ // returns the S3 key before AWS has finished writing the object. Downloading immediately
+ // races the report generation and fails with NoSuchKey ("The specified key does not
+ // exist"). Poll for the object with backoff until it appears (or we time out).
+ var deadline = DateTime.UtcNow.AddMinutes(3);
+ var delay = TimeSpan.FromSeconds(2);
+ while (true)
{
- BucketName = bucket,
- Key = key
- }, cancellationToken).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+ try
+ {
+ using var response = await s3.GetObjectAsync(new GetObjectRequest
+ {
+ BucketName = bucket,
+ Key = key
+ }, cancellationToken).ConfigureAwait(false);
- using var reader = new StreamReader(response.ResponseStream);
- return await reader.ReadToEndAsync().ConfigureAwait(false);
+ using var reader = new StreamReader(response.ResponseStream);
+ return await reader.ReadToEndAsync().ConfigureAwait(false);
+ }
+ catch (AmazonS3Exception ex) when (
+ (ex.ErrorCode == "NoSuchKey" || ex.StatusCode == System.Net.HttpStatusCode.NotFound)
+ && DateTime.UtcNow < deadline)
+ {
+ Logger.LogDebug(
+ $"Audit report not yet available in S3 (bucket={bucket}, key={key}); " +
+ $"retrying in {delay.TotalSeconds:0}s.");
+ await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
+ delay = TimeSpan.FromSeconds(Math.Min(delay.TotalSeconds * 1.5, 15));
+ }
+ }
}
private async Task GetOrCreateS3ClientAsync(CancellationToken cancellationToken)
@@ -781,4 +804,4 @@ private static class ConfigKeys
public const string Region = "Region";
public const string S3Bucket = "S3Bucket";
}
-}
+}
\ No newline at end of file
diff --git a/docsource/configuration.md b/docsource/configuration.md
index f15dd50..580cfc6 100644
--- a/docsource/configuration.md
+++ b/docsource/configuration.md
@@ -8,10 +8,9 @@ This integration is tested and confirmed as working for Anygateway REST 24.4 and
## Gateway Registration
Download the **PCA root certificate** from AWS and have it ready to import into the Gateway **in `.pem` format**.
-## Authentication (Access Key + Secret)
+## Authentication
-The CAPlugin currently supports **one** authentication method: **AWS Access Key ID + Secret Access Key**.
-**OAuth** and **Default SDK authentication** will be enabled in later updates. There is functionality present via the **Keyfactor AWS Authentication** library, but these alternate methods are currently ***untested***.
+The CAPlugin authenticates to AWS through the **Keyfactor AWS Authentication** library, which supports several methods. All of the methods below are supported and validated against ACM PCA. Choose **one** method per CA connection by setting the corresponding toggles/fields in **CAConnection**.
### Known Issues
@@ -20,83 +19,116 @@ Latest version of Command 25.4 may run into issues, investigation into compatibi
### What you need ready
-Before configuring the CAPlugin, have the following prepared:
+> **Key concept — the *destination* identity.** Whichever identity the plugin ends up operating as — the assumed `RoleArn`, or the *originating* identity when no Assume Role is performed — is the **destination**, and it is what must hold the ACM PCA + S3 audit-report permissions (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)). For every Assume-Role method the **originating** identity additionally needs `sts:AssumeRole` on the target role, and the target role's **trust policy** must allow that originating principal (see [Example trust policies](#example-trust-policies)).
-#### 1) IAMUserAccessKey and IAMUserAccessSecret
-- **Access Key ID** (example format: `AKIAIOSFODNN7EXAMPLE`)
-- **Secret Access Key** (example format: `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`)
+### Common requirements (all methods)
-#### 2) A target IAM Role the Gateway will run as (recommended)
-Example:
-- `arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`
+- **Region** — the AWS region for service endpoints; must match your **ACM Private CA** region (e.g. `us-east-2`).
+- **CAArn** — the Certificate Authority ARN, e.g. `arn:aws:acm-pca:::certificate-authority/`.
+- **S3Bucket** — bucket name (not ARN) for ACM PCA audit reports, e.g. `keyfactor-acmpca-audit-reports`. The destination identity needs `s3:ListBucket`/`s3:GetBucketLocation` on the bucket and `s3:GetObject`/`s3:PutObject` on `/*`, and ACM PCA itself must be allowed to write to it (see [Example policy for bucket](#example-policy-for-bucket)).
+- **PCA Root Cert** — download the PCA root certificate from AWS in `.pem` format to upload under **Gateway Registration**.
+- **Scan interval** — ACM PCA limits audit-report generation to roughly **once per 30 minutes** per CA. Set the CA's `ServiceSettings` full-scan interval to **≥ 30 minutes** to avoid throttling.
-**Role expectations:**
-- The role must have permissions for:
- - **ACM PCA operations** (Issue/Get/Revoke/Describe + CA certificate chain retrieval)
- - **Audit report creation & status polling** (Create/Describe audit reports)
- - **S3 bucket access** to read/write audit report objects
+### Method configuration
-#### 3) Permissions on the assumed role
-The assumed role must have permissions for the AWS services the Gateway needs. This typically includes:
-- `acm-pca:*` actions required for enrollment + revocation workflows
-- Audit report actions (`acm-pca:CreateCertificateAuthorityAuditReport`, `acm-pca:DescribeCertificateAuthorityAuditReport`)
-- S3 bucket and object access for the audit report destination bucket
+For every method, also set the common `Region`, `CAArn`, and `S3Bucket`, and upload the Root CA under **Gateway Registration**. Set all auth toggles you are **not** using to `false`.
-**See the example IAM policies below in this README section**
+#### IAM User (Access Key + Secret)
+- `UseIAM` = `true`
+- `IAMUserAccessKey` (e.g. `AKIAIOSFODNN7EXAMPLE`), `IAMUserAccessSecret` (e.g. `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`)
+- `RoleArn` — the destination role to assume (e.g. `arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`)
-#### 4) Region
-Know the **AWS region** the connector should use (for service endpoints), e.g.:
-- `us-east-1`
+**AWS setup:** the IAM user needs `sts:AssumeRole` on `RoleArn`; `RoleArn`'s trust policy must allow the IAM user (see [Assume-Role trust](#example-trust-policies)); `RoleArn` holds the [PCA + S3 permissions](#example-iam-policies-for-the-destination-identity).
-> Region must match the region of your **ACM Private CA**.
+#### IAM User + ExternalId
+As **IAM User**, plus set `ExternalId`. The target role's trust policy must include a matching `sts:ExternalId` condition (see [Assume-Role with ExternalId](#example-trust-policies)). Use this when the role owner requires a shared secret to prevent the confused-deputy problem.
-#### 5) CA ARN
-Have the **Certificate Authority ARN** for the PCA you want to integrate with.
+#### Default SDK (no assume)
+- `UseDefaultSdkAuth` = `true`, `DefaultSdkAssumeRole` = `false` (`RoleArn` is ignored)
-Example format:
-- `arn:aws:acm-pca:::certificate-authority/`
+The Gateway host's **ambient AWS identity** is the destination and must hold the PCA + S3 permissions directly. The AWS SDK resolves this from, in order: `AWS_*` environment variables, the shared credentials file `[default]` profile, or an EC2 instance role. On a Windows AnyCA Gateway host the service runs as `NETWORK SERVICE`, so provide credentials via the machine-level env var `AWS_SHARED_CREDENTIALS_FILE` (pointing at a credentials file readable by the service) or the service account's `%USERPROFILE%\.aws\credentials`.
-Example:
-- `arn:aws:acm-pca:us-east-2:123456789012:certificate-authority/11111111-2222-3333-4444-555555555555`
+#### Default SDK + Assume Role
+- `UseDefaultSdkAuth` = `true`, `DefaultSdkAssumeRole` = `true`, `RoleArn` = destination role
-#### 6) S3 Bucket
-Choose an S3 bucket to store / retrieve ACM PCA audit reports.
+The ambient identity (resolved as above) is the **originating** account and performs `sts:AssumeRole` into `RoleArn`. The ambient identity needs `sts:AssumeRole`; `RoleArn`'s trust allows it and holds the PCA + S3 permissions.
-You should have:
-- **Bucket name** (example: `keyfactor-acmpca-audit-reports`, not the full bucket ARN!)
+#### Credential Profile (± Assume Role)
+Prefix the `RoleArn` with a `[profile-name]`, e.g. `[myprofile]arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`, and set `UseDefaultSdkAuth` = `true`. The named profile must exist in the shared AWS credentials file **on the Gateway host** (same location rules as Default SDK above).
+- **No assume** (`DefaultSdkAssumeRole` = `false`): the profile identity is the destination and needs the PCA + S3 permissions directly.
+- **With assume** (`DefaultSdkAssumeRole` = `true`): the profile is the originating identity and assumes `RoleArn` (which holds the permissions and trusts the profile identity).
-> The role needs `s3:ListBucket` / `s3:GetBucketLocation` at the bucket ARN, and `s3:GetObject` / `s3:PutObject` on the object ARN pattern.
+#### OAuth (OIDC federation)
+- `UseOAuth` = `true`
+- `OAuthClientId`, `OAuthClientSecret`, `OAuthUrl` (the token endpoint), `OAuthScope`, `OAuthGrantType` (`client_credentials`)
+- `RoleArn` = destination role (federated web-identity role)
-#### 7) PCA Root Cert
-Download the **PCA root certificate** from AWS and have it ready to import into the Gateway **in `.pem` format**.
+The plugin requests an OAuth token (`client_credentials` grant, client id/secret sent as an HTTP **Basic** `Authorization` header) and calls `sts:AssumeRoleWithWebIdentity` on `RoleArn`.
+
+**AWS setup:** create an IAM **OIDC identity provider** for the token issuer, and set `RoleArn`'s trust policy to allow `sts:AssumeRoleWithWebIdentity` for that provider with the appropriate `aud` (and optionally `sub`) conditions (see [OAuth/web-identity trust](#example-trust-policies)).
+
+> OAuth notes: the OIDC provider must accept the client id/secret as a **Basic auth header** (not POST body); **DPoP must be disabled** on the app; and the token lifetime must be **≤ 12 hours** — STS `AssumeRoleWithWebIdentity` caps `DurationSeconds` at 43200, and the plugin derives the session duration from the token's `expires_in`.
+
+### Setting up AWS Authentication (Examples)
+
+> [!NOTE]
+> Several different options are offered for authenticating with AWS.
+> Documentation for how these options work is maintained in the [aws-auth-library](https://github.com/Keyfactor/aws-auth-library) repository.
+
+The following examples show potential configurations for Roles in AWS with different selected authentication methods. Your configuration steps may differ depending on the specific requirements of your use case. In every case the **destination identity** must hold the ACM Private CA + S3 audit-report permissions (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)), and every Assume-Role method additionally requires the *originating* identity to have `sts:AssumeRole` and the target role's **trust policy** to allow that principal (see [Example trust policies](#example-trust-policies)).
+
+
+Host instance credentials using Default SDK and Assume Role
+
+Select the `Use Default SDK Auth` option (`UseDefaultSdkAuth=true`) to have the plugin load the Gateway host's ambient AWS credentials. If the Gateway runs on an EC2 instance, this is the IAM Role assigned to the instance; on a non-EC2 Windows host it is whatever the service account resolves from `AWS_SHARED_CREDENTIALS_FILE` or `%USERPROFILE%\.aws\credentials` (the AnyCA Gateway service runs as `NETWORK SERVICE`).
+
+If that ambient identity is itself the Destination account identity to use with ACM Private CA, no additional Role needs to be configured. If it is only to be used initially and a separate `RoleArn` is designated as the Destination account, also select `Assume new Role using Default SDK Auth` (`DefaultSdkAssumeRole=true`).
+
+**AWS Setup**
+_Note: to use instance credentials, the CAPlugin's Gateway host must be running inside an EC2 instance._
+1. Assign or note the existing IAM Role assigned to the EC2 instance (or the credentials configured for the service account).
+2. If Assume Role is used, ensure a [Trust Relationship](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/edit_trust.html) is set up on the Destination role that allows the ambient identity to assume it.
+3. Verify the permissions match the requirements for accessing ACM Private CA (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)).
+
+
+
+
+OAuth OIDC Identity Provider (Okta example)
+
+Select the `Use OAuth` option (`UseOAuth=true`) for a CA connection to use an OAuth Identity Provider, and supply `OAuthClientId`, `OAuthClientSecret`, `OAuthUrl` (token endpoint), `OAuthScope`, and `OAuthGrantType` (`client_credentials`).
+
+**AWS Setup**
+1. A 3rd party [Identity Provider](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) — an IAM **OIDC identity provider** for the token issuer — needs to be set up in AWS.
+2. An [AWS Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html) (the `RoleArn`) needs to be created for use with your Identity Provider.
+3. Ensure the [Trust Relationship](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/edit_trust.html) is set up for that role with the Identity Provider, allowing `sts:AssumeRoleWithWebIdentity` with the appropriate `aud` (and optionally `sub`) conditions (see [OAuth / web-identity trust](#example-trust-policies)).
+4. Verify the permissions match the requirements for accessing ACM Private CA.
+
+**OKTA Setup**
+1. Ensure your Authorization Server is set up in Okta.
+2. Ensure the appropriate scopes are set up in Okta.
+3. Set up an Okta App (client-credentials / API Services type). The app must send the client id/secret as a **Basic auth header** (not POST body), have **DPoP disabled**, and issue tokens with a lifetime **≤ 12 hours** (STS caps `DurationSeconds` at 43200).
+
+
-### Enabling all this in the Gateway Configuration Portal
+
+IAM User credentials to Assume Role
-#### 1) Register the Gateway CA and upload the Root CA certificate
-1. Navigate to **Gateway Registration**.
-2. Upload the **Root CA Certificate** you downloaded earlier (PEM).
+Select the `Use IAM` option (`UseIAM=true`) for a CA connection to use an IAM User credential, supplying `IAMUserAccessKey` and `IAMUserAccessSecret`.
-#### 2) Configure the CA connection settings
-1. Navigate to **CAConnection**.
-2. Populate:
- - `RoleArn` (example: `arn:aws:iam::123456789012:role/Keyfactor-AnyGateway-AcmPcaRole`)
- - `Region` (example: `us-east-2`)
- - `CAArn` (example: `arn:aws:acm-pca:us-east-2:123456789012:certificate-authority/11111111-2222-3333-4444-555555555555`)
- - `S3Bucket` (example: `keyfactor-acmpca-audit-reports`)
- - `IAMUserAccessKey` (example: `AKIA...`)
- - `IAMUserAccessSecret` (example: `wJalrXU...`)
+**AWS Setup**
+1. An [AWS Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html) to Assume with your IAM User needs to be created (set as `RoleArn`).
+2. Ensure a [Trust Relationship](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/edit_trust.html) is set up for that role that allows the IAM User (see [Assume-Role trust](#example-trust-policies)). If using the ExternalId modifier, add a matching `sts:ExternalId` condition.
+3. AWS does not support programmatic access for AWS SSO accounts. The account used here must be a standard AWS IAM User with an Access Key credential type.
+4. Verify the permissions match the requirements for accessing ACM Private CA (see [Example IAM policies](#example-iam-policies-for-the-destination-identity)).
-3. Set these auth toggles:
- - `UseDefaultSdkAuth` = `false`
- - `UseOAuth` = `false`
- - `UseIAM` = `true`
+
---
-### Example IAM policies for the assumed role
+### Example IAM policies for the destination identity
-The following examples are intended as **copy/adapt templates**.
+These permissions belong on the **destination identity** (the assumed `RoleArn`, or the ambient/profile identity when no Assume Role is performed). The following examples are intended as **copy/adapt templates**.
#### Example 1: Minimal PCA issuance/retrieval/revocation
@@ -171,6 +203,70 @@ The following examples are intended as **copy/adapt templates**.
```
---
+### Example trust policies
+
+Every Assume-Role method requires a **trust policy** on the destination role that allows the originating principal to assume it. (Default SDK *no-assume* and Credential Profile *no-assume* need no trust policy — the identity is used directly.)
+
+#### Assume Role — IAM User, Default SDK + Assume, or Credential Profile + Assume
+
+`<>` is the IAM user ARN (IAM User method) or the ambient/profile identity ARN (Default SDK / Credential Profile + Assume).
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": { "AWS": "<>" },
+ "Action": "sts:AssumeRole"
+ }
+ ]
+}
+```
+
+#### Assume Role with ExternalId
+
+Add a matching `sts:ExternalId` condition; the CAConnection `ExternalId` value must equal it.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": { "AWS": "<>" },
+ "Action": "sts:AssumeRole",
+ "Condition": { "StringEquals": { "sts:ExternalId": "<>" } }
+ }
+ ]
+}
+```
+
+#### OAuth — web-identity trust
+
+`RoleArn` for the OAuth method must trust the OIDC provider you registered for your token issuer. Condition on the token `aud` (audience) and optionally `sub` (for `client_credentials`, `sub` is typically the client id).
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": { "Federated": "arn:aws:iam:::oidc-provider//" },
+ "Action": "sts:AssumeRoleWithWebIdentity",
+ "Condition": {
+ "StringEquals": {
+ "/:aud": "<>",
+ "/:sub": "<>"
+ }
+ }
+ }
+ ]
+}
+```
+
+---
+
### Example policy for bucket
```json
{