Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
Expand Down
90 changes: 59 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,35 @@ The DLL ships **two validator types**:

When configuring a Domain Validation Configuration in the gateway UI, pick the validator type that matches the CA's requirement — TXT/`dns-01` for ACME, CNAME/`cname` for CAs that validate via CNAME. Both share the same Google Cloud credentials and configuration fields.

The plugin uses the [Google Cloud DNS API](https://cloud.google.com/dns/docs/reference/rest/v1) via the official `Google.Apis.Dns.v1` SDK. Records are applied through the Changes API. For TXT staging, existing values at the same name are preserved and the new value is appended, so co-existing ACME challenges (a wildcard and the apex domain both producing `_acme-challenge` TXT values) coexist. For CNAME staging, the record set is replaced since a CNAME is singular per name. The owning zone for an FQDN is resolved by selecting the managed zone whose `dnsName` is the longest matching suffix of the record name.
The plugin uses the [Google Cloud DNS API](https://cloud.google.com/dns/docs/reference/rest/v1) via the official `Google.Apis.Dns.v1` SDK. Records are applied through the Changes API (a delete of the prior record set plus an add of the new one). For TXT staging, existing values at the same name are preserved and the new value is appended, so co-existing ACME challenges (a wildcard and the apex domain both producing `_acme-challenge` TXT values) coexist. For CNAME staging, the record set is replaced since a CNAME is singular per name by DNS rules. On cleanup, the record set for the managed type/name is removed (for TXT, if a specific value is supplied only that value is removed and any co-existing values are preserved).

The owning zone for a given FQDN is resolved by listing the Cloud DNS managed zones in the configured project and selecting the one whose `dnsName` is the longest matching suffix of the record name.

## Features

- Automated DNS TXT (dns-01) and CNAME (cname) record creation and deletion in Google Cloud DNS
- Multiple credential options: Service Account JSON, Service Account key file, or Application Default Credentials / Workload Identity
- Automatic managed-zone discovery by longest DNS-name suffix match
- Automated DNS TXT record creation and deletion in Google Cloud DNS

## Requirements

### Keyfactor Platform
- Keyfactor AnyCA Gateway REST **26.2 or later** (DNS validation support was added in AnyCA Gateway 26.2)
- A gateway product that supports DNS-01 domain validation (ACME REST Gateway, DigiCert, Sectigo, etc.)

### Google Cloud DNS Requirements

### Keyfactor platform

- Keyfactor AnyCA Gateway REST **26.2 or later** (DNS validation support was added in AnyCA Gateway 26.2)
- A gateway product that supports DNS-based domain validation (ACME REST Gateway, DigiCert, Sectigo, SSL Store, etc.)

### Google Cloud Requirements
### Google Cloud requirements

1. A Google Cloud project with **Cloud DNS** enabled and one or more managed zones for the domains being validated.
2. A principal with permission to read zones and write record sets — the built-in **DNS Administrator** role (`roles/dns.admin`) is sufficient; a custom role needs at least `dns.managedZones.list`, `dns.resourceRecordSets.*`, and `dns.changes.create`.
3. Credentials via one of (resolution order): Service Account key JSON, Service Account key file, or Application Default Credentials / Workload Identity.
2. A principal the plugin can authenticate as, with permission to read zones and write record sets. The built-in role **DNS Administrator** (`roles/dns.admin`) is sufficient; a custom role needs at least `dns.managedZones.list`, `dns.resourceRecordSets.*`, and `dns.changes.create`.
3. Credentials, provided by one of (in resolution order):
- **Service Account key JSON** (`Google_ServiceAccountKeyJson`) — recommended for gateways not running on GCP.
- **Service Account key file** (`Google_ServiceAccountKeyPath`) — a path to the JSON key on the gateway host.
- **Application Default Credentials / Workload Identity** — used automatically when neither key is supplied (ideal when the gateway runs on GCP).

### Configuration fields

Expand All @@ -79,62 +89,63 @@ The plugin uses the [Google Cloud DNS API](https://cloud.google.com/dns/docs/ref

* The plugin sets the record TTL to 60 seconds.
* Zones are discovered from the configured project only; a record whose domain is not covered by a managed zone in that project fails with `No Google Cloud DNS managed zone found`.
* Each validator type manages only its own record type: `GoogleDomainValidator` reads/writes `TXT`, `GoogleCnameDomainValidator` reads/writes `CNAME`.
* Each validator type manages only its own record type: `GoogleDomainValidator` reads/writes `TXT`, `GoogleCnameDomainValidator` reads/writes `CNAME`. Neither touches other record types.
* Cloud DNS stores TXT rdata quoted; the plugin quotes values automatically and will not double-quote an already-quoted value.

### Runtime Requirements
- .NET runtime provided by the gateway server (the plugin targets net6.0, net8.0, and net10.0)
- .NET 10.0 runtime (provided by the gateway server)

## Installation

This plugin is installed alongside any Keyfactor gateway server that supports DNS-based domain validation (ACME REST Gateway, DigiCert, Sectigo, SSL Store, etc.). The same DLL works with every supported gateway.
This plugin is installed alongside any Keyfactor gateway server that supports DNS-01 domain validation (ACME REST Gateway, DigiCert, Sectigo, etc.). The same DLL works with every supported gateway.

> See the official Keyfactor AnyCA Gateway REST installation documentation for the authoritative install instructions. The steps below are a general guide; defer to the official docs if they diverge.
> See the official Keyfactor AnyCA Gateway REST installation documentation for the authoritative install instructions: **<TBD link from Sarah Duncan>**. The steps below are a general guide; defer to the official docs if they diverge.

### 1. Download the Plugin

Download the latest release from the [Releases](https://github.com/Keyfactor/google-cloud-dnsplugin/releases) page.

### 2. Copy the plugin DLLs to the gateway's Extensions folder

On the server hosting your gateway, unzip the release and copy the contents of the framework directory that matches your gateway (e.g. `net8.0`) into the gateway's `Extensions` folder.
On the server hosting your gateway, unzip the release and copy the contents of the `net10.0` directory into the gateway's `Extensions` folder.

**Windows** (example path — substitute the gateway product folder for your install):

```text
C:\Program Files\Keyfactor\<GatewayName>\AnyGatewayREST\net8.0\Extensions\
C:\Program Files\Keyfactor\<GatewayName>\AnyGatewayREST\net10.0\Extensions\
```

**Linux**:

```text
/opt/keyfactor/<gateway-name>/AnyGatewayREST/net8.0/Extensions/
/opt/keyfactor/<gateway-name>/AnyGatewayREST/net10.0/Extensions/
```

Replace `<GatewayName>` (or `<gateway-name>` on Linux) with the gateway you are installing into (e.g. `AcmeGwDns`, `DigiCert`, `Sectigo`, `SslStoreGw`).
Replace `<GatewayName>` (or `<gateway-name>` on Linux) with the gateway you are installing into (e.g. `AcmeGwDns`, `DigiCert`, `Sectigo`).

### 3. Restart the gateway service

Restart the AnyGatewayREST service for the gateway you installed the plugin into so the Extensions folder is rescanned.
Restart the AnyGatewayREST Windows service for the gateway you installed the plugin into so the Extensions folder is rescanned.

## Configuration

After installing the plugin DLL into the gateway's Extensions folder, configure a new Domain Validation entry in the AnyCA Gateway REST UI and select **Google Cloud DNS** (`GoogleDomainValidator` for TXT/dns-01, or `GoogleCnameDomainValidator` for CNAME) as the provider type, then map it to the domain(s) it should manage.
After installing the plugin DLL into the gateway's Extensions folder, configure a new DNS Provider entry in the AnyCA Gateway REST UI and select **Google Cloud** as the provider type. See the official Keyfactor AnyCA Gateway REST documentation for the canonical UI walkthrough: **<TBD link from Sarah Duncan>**.

### Configuration Parameters

| Parameter | Description | Required | Example |
|-----------|-------------|----------|---------|
| `Google_ProjectId` | Google Cloud project ID that contains the Cloud DNS managed zones. | Yes | `my-gcp-project` |
| `Google_ServiceAccountKeyJson` | Service Account key as JSON (stored as a secret). | No | ` ` |
| `Google_ServiceAccountKeyPath` | Path to a Service Account JSON key file on the gateway host. | No | `/etc/keyfactor/gcp-sa.json` |
| `Google_ProjectId` | Google Cloud project ID that contains the Cloud DNS managed zones. | Yes | ` ` |
| `Google_ServiceAccountKeyJson` | Service Account key as JSON. Optional — leave empty to use a key file or Application Default Credentials / Workload Identity. Stored as a secret. | No | ` ` |
| `Google_ServiceAccountKeyPath` | Path to a Service Account JSON key file on the gateway host. Used only when the JSON key is not provided. Leave empty to use Application Default Credentials. | No | ` ` |

### Example Configuration

```json
{
"Google_ProjectId": "my-gcp-project",
"Google_ProjectId": "",
"Google_ServiceAccountKeyJson": "",
"Google_ServiceAccountKeyPath": "/etc/keyfactor/gcp-sa.json"
"Google_ServiceAccountKeyPath": ""
}
```

Expand All @@ -144,24 +155,41 @@ After installing the plugin DLL into the gateway's Extensions folder, configure

Once configured, the plugin automatically handles DNS validation during certificate enrollment and renewal:

1. **Record Creation**: the gateway calls the plugin to publish the validation record (TXT or CNAME) in the appropriate Cloud DNS managed zone.
2. **Validation**: the CA verifies the record and issues the certificate.
3. **Cleanup**: the gateway calls the plugin to remove the validation record once the order is issued.
1. **Record Creation**: Plugin creates a DNS TXT record with the validation challenge
2. **Propagation Wait**: Plugin waits for DNS propagation
3. **Verification**: Plugin verifies the record exists on Google Cloud DNS nameservers
4. **Cleanup**: Plugin deletes the validation record after successful validation

### Zone Discovery

The plugin discovers the managed zone for a domain by longest matching `dnsName` suffix within the configured project:
The plugin automatically discovers the appropriate DNS zone for a domain:

- For `_acme-challenge.www.example.com`, a managed zone for `example.com.` is matched.
- For `*.example.com`, the `example.com.` zone is matched.
- For `www.example.com`, searches for zones: `www.example.com`, `example.com`
- For `sub.example.com`, searches for zones: `sub.example.com`, `example.com`
- For `*.example.com`, searches for zones: `example.com`

## Troubleshooting

### Common Issues

- **Authentication Failures**: verify the Service Account key/ADC is valid and that the principal has `roles/dns.admin` (or the equivalent custom permissions) on the project.
- **Zone Not Found**: verify the target domain is covered by a Cloud DNS managed zone in the configured `Google_ProjectId`.
- **Permission Denied**: confirm the principal can list managed zones and create changes/record sets.
- **Authentication Failures**: Verify Google Cloud DNS credentials are valid, not expired, and authorized for the target zone.
- **Insufficient Permissions**: Verify the account/role has the documented minimum permissions on the target DNS zone.
- **Zone Not Found**: Verify the target DNS zone exists in your Google Cloud DNS account and is reachable from the gateway server.
- **DNS Propagation Timeouts**: Check Google Cloud DNS service health; verify authoritative nameservers are responding.

### Logging

Enable debug logging in the gateway's logging configuration:

```json
{
"Logging": {
"LogLevel": {
"Keyfactor.Extensions.DomainValidator.Google": "Debug"
}
}
}
```

## Support

Expand Down