Skip to content

Commit afc752c

Browse files
Merge 4d9ff86 into 38354fb
2 parents 38354fb + 4d9ff86 commit afc752c

10 files changed

Lines changed: 2139 additions & 325 deletions

File tree

.claude/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(git fetch:*)",
5+
"Bash(git checkout:*)"
6+
]
7+
}
8+
}

README.md

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ This integration is tested and confirmed as working for Anygateway REST 24.2 and
8686
* **ApiKey** - CSCGlobal API Key
8787
* **BearerToken** - CSCGlobal Bearer Token
8888
* **DefaultPageSize** - Default page size for use with the API. Default is 100
89-
* **TemplateSync** - Enable template sync.
9089
* **SyncFilterDays** - Number of days from today to filter certificates by expiration date during incremental sync.
90+
* **RenewalWindowDays** - Number of days before the annual order expiry within which a RenewOrReissue triggers a paid Renewal rather than a free Reissue. Default is 30.
91+
* **DcvPollTimeoutSeconds** - Max seconds to synchronously poll CSC for issuance after submitting an order (and publishing CNAME DCV). 0 disables polling (enrollment returns pending immediately; cert arrives on next sync). When >0, fast-validating orders can return the cert directly. Keep small to avoid long-blocking enrollment requests.
9192

9293
2. PLEASE NOTE, AT THIS TIME THE RAPID_SSL TEMPLATE IS NOT SUPPORTED BY THE CSC API AND WILL NOT WORK WITH THIS INTEGRATION
9394

@@ -311,6 +312,114 @@ This integration is tested and confirmed as working for Anygateway REST 24.2 and
311312
* **Addtl Sans Comma Separated DCV Emails** - OPTIONAL: Additional SANs DCV Emails, comma separated
312313

313314

315+
## CA Connection Configuration
316+
317+
When defining the Certificate Authority in the AnyCA Gateway REST portal, configure the following fields on the **CA Connection** tab:
318+
319+
CONFIG ELEMENT | DESCRIPTION | DEFAULT
320+
---------------|-------------|--------
321+
CscGlobalUrl | The base URL for the CSCGlobal API (e.g. `https://apis.cscglobal.com`) | (required)
322+
ApiKey | Your CSCGlobal API key | (required)
323+
BearerToken | Your CSCGlobal Bearer token for authentication | (required)
324+
DefaultPageSize | Page size for API list requests | 100
325+
SyncFilterDays | Number of days from today used to filter certificates by expiration date during **incremental** sync. Only certificates expiring within this window are returned. Does not apply to full sync. | 5
326+
RenewalWindowDays | Number of days before the annual order expiry date within which a **RenewOrReissue** request triggers a paid **Renewal** rather than a free **Reissue**. See [Renewal vs. Reissue Logic](#renewal-vs-reissue-logic) below. | 30
327+
DcvPollTimeoutSeconds | Max seconds to synchronously poll CSC for certificate issuance after submitting an order. `0` disables polling (enrollment returns pending immediately; cert arrives on the next sync). When `>0`, fast-validating orders can return the issued cert directly in the enrollment response. See [Synchronous Issuance Polling](#synchronous-issuance-polling) below. | 0
328+
329+
> **Note:** DNS auto-publishing for CNAME DCV is handled by the AnyCA Gateway REST framework's Domain Validation system (gateway 3.3+). It's configured in the gateway UI under **Domain Validation Configurations**, not on the CA Connection tab. See [DNS Auto-Publishing (CNAME DCV)](#dns-auto-publishing-cname-dcv).
330+
331+
## Renewal vs. Reissue Logic
332+
333+
CSC Global subscriptions are annual orders. When Keyfactor Command sends a **RenewOrReissue** request, the plugin must decide whether to submit a **Renewal** (a new paid order) or a **Reissue** (a free re-key under the existing active order).
334+
335+
The decision is based on the **RenewalWindowDays** setting and works as follows:
336+
337+
1. The plugin fetches the original certificate from CSC and reads its `orderDate`.
338+
2. It computes the **order expiry** as `orderDate + 1 year`.
339+
3. It calculates **days remaining** until the order expires.
340+
4. If `days remaining <= RenewalWindowDays`, the request is treated as a **Renewal** (new paid order).
341+
5. If `days remaining > RenewalWindowDays`, the request is treated as a **Reissue** (free under the active order).
342+
343+
**Example with default RenewalWindowDays = 30:**
344+
345+
```
346+
Order Date: 2025-04-08
347+
Order Expiry: 2026-04-08
348+
Today: 2026-03-15
349+
Days Left: 24
350+
351+
24 <= 30 --> RENEWAL (new paid order)
352+
```
353+
354+
```
355+
Order Date: 2025-04-08
356+
Order Expiry: 2026-04-08
357+
Today: 2025-09-01
358+
Days Left: 219
359+
360+
219 > 30 --> REISSUE (free under active order)
361+
```
362+
363+
**Fallback behavior:** If the plugin cannot retrieve the `orderDate` from CSC (e.g., API error or missing field), it falls back to checking the certificate's expiration date. If the certificate is already expired, it treats the request as a Renewal.
364+
365+
**Note:** Both Renewal and Reissue submissions are asynchronous at CSC. The plugin returns a "pending" status and the issued certificate will appear in Keyfactor after the next sync cycle.
366+
367+
## DNS Auto-Publishing (CNAME DCV)
368+
369+
CSC supports two Domain Control Validation (DCV) methods: **EMAIL** and **CNAME**. With CNAME validation, CSC returns a CNAME record (name → target) that must exist in DNS before they will validate the order.
370+
371+
By default this plugin returns the CNAME details to Keyfactor Command for **manual publishing**. To fully automate enrollment, the plugin uses the **AnyCA Gateway REST framework's built-in DNS provider system** (available in framework 3.3 and later). The framework discovers DNS provider plugins deployed alongside the CA plugin and routes each CNAME to whichever provider claims the matching DNS zone.
372+
373+
### Requirements
374+
375+
* AnyCA Gateway REST framework **3.3 or later** (the `IDomainValidatorFactory` interface ships in `Keyfactor.AnyGateway.IAnyCAPlugin` 3.3+).
376+
* At least one DNS provider DLL (e.g. GoDaddy, Cloudflare, Route 53, Azure) deployed in the gateway `Extensions` folder.
377+
* A Domain Validation Configuration registered in the gateway UI that maps your domain(s) to the deployed provider (for example, `*.example.com` → GoDaddy).
378+
379+
### How It Works
380+
381+
1. CSC returns the CNAME `name → target` details in the enrollment response.
382+
2. For each CNAME entry, the plugin calls `IDomainValidatorFactory.ResolveDomainValidator(recordName, "cname")`.
383+
3. The framework returns the `IDomainValidator` whose Domain Validation Configuration matches the record's zone (or `null` if no match).
384+
4. The plugin calls `validator.StageValidation(recordName, cnameTarget, ct)` to publish the record.
385+
5. CSC asynchronously validates the CNAME; the issued certificate appears on the next sync.
386+
387+
### Behavior
388+
389+
* **Resolution is per record, not per CA.** One CA can drive multiple DNS providers (GoDaddy for some domains, Route 53 for others) with no per-CA configuration.
390+
* **Only invoked for CNAME DCV.** Templates configured with EMAIL validation are unaffected — no DNS publishing occurs.
391+
* **Best-effort.** If no provider claims the zone, the publish call fails, or the factory wasn't injected (gateway pre-3.3), the enrollment still succeeds and the CNAME details remain in the Keyfactor request so a human can publish manually as a fallback.
392+
* **Trace-logged.** Every resolution (matched/unresolved) and publish attempt (success/failure) is logged at Info/Trace level.
393+
* **Validation type string.** The plugin passes `"cname"` to `ResolveDomainValidator`. CSC's DCV requires a **CNAME** record, which is different from ACME's `"dns-01"` challenge (a TXT record). A single DNS provider DLL can ship multiple validator classes — one advertising `"dns-01"` (publishes TXT, for ACME) and one advertising `"cname"` (publishes CNAME, for CSC). You must deploy and configure a validator that advertises `"cname"` or no provider will match.
394+
* **Trailing dots normalized.** CSC returns FQDN-canonical names with a trailing dot (e.g. `_token.example.com.`). The plugin strips the trailing dot before resolution and publishing, because Domain Validation Configurations and DNS provider APIs expect names without it.
395+
396+
### Configuration in the Gateway UI
397+
398+
In the AnyCA Gateway REST portal, under **Domain Validation Configurations**:
399+
400+
1. **Add** a new configuration.
401+
2. Pick a **Domain Validator Type** that publishes **CNAME** records and advertises validation type `cname`. For GoDaddy this is `GoDaddyCnameDomainValidator` (the `GoDaddyDomainValidator` variant publishes TXT for ACME and will **not** work for CSC).
402+
3. Add one or more **domain patterns** (e.g. `*.example.com`).
403+
4. Fill out the provider-specific **Configuration Settings** (API keys, base URL, etc.).
404+
5. Save.
405+
406+
Once configured, any CSC enrollment for a domain matching one of those patterns will have its CNAME auto-published.
407+
408+
> **Common pitfall:** If you configure the TXT/`dns-01` validator (e.g. `GoDaddyDomainValidator`) for a CSC domain, the record will publish as a **TXT** and CSC's CNAME validation will never succeed. Make sure you select the **CNAME** validator variant.
409+
410+
## Synchronous Issuance Polling
411+
412+
CSC validates domain control asynchronously — after an order is submitted (and the CNAME DCV record published), CSC/Sectigo polls public DNS on its own schedule and issues the certificate once validation passes. By default this plugin returns a **pending** (`EXTERNALVALIDATION`) result immediately and the issued certificate is picked up on the next gateway **sync** cycle.
413+
414+
For environments where DNS is published automatically (see [DNS Auto-Publishing](#dns-auto-publishing-cname-dcv)) and validation tends to complete quickly, you can have the plugin **poll CSC synchronously** at the end of enrollment and return the issued certificate directly — avoiding the wait for the next sync.
415+
416+
* Set **`DcvPollTimeoutSeconds`** to the maximum number of seconds to poll (e.g. `60`). `0` (default) disables polling entirely.
417+
* The plugin polls CSC every 10 seconds until the order is issued or the timeout is reached.
418+
* If the certificate issues within the window, the enrollment returns it immediately with a success status.
419+
* If the window expires, the plugin falls back to the **pending** result and the certificate arrives on the next sync — exactly as it would with polling disabled.
420+
421+
**Tradeoff:** Polling blocks the enrollment request for up to `DcvPollTimeoutSeconds`. CSC validation frequently takes minutes to hours, so most orders will still fall through to pending — keep the timeout small (30–90s) to catch only the fast cases without hanging callers. This applies to New enrollments, Renewals, and Reissues.
422+
314423
315424
## License
316425

0 commit comments

Comments
 (0)