Skip to content

Commit a76c620

Browse files
saas fixes
1 parent b28cc5b commit a76c620

File tree

7 files changed

+129
-20
lines changed

7 files changed

+129
-20
lines changed

AcmeCaPlugin/AcmeCaPluginConfig.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ public static Dictionary<string, PropertyConfigInfo> GetPluginAnnotations()
6060
DefaultValue = "",
6161
Type = "String"
6262
},
63+
["Google_ServiceAccountKeyJson"] = new PropertyConfigInfo()
64+
{
65+
Comments = "Google Cloud DNS: Service account JSON key content (alternative to file path for containerized deployments)",
66+
Hidden = true,
67+
DefaultValue = "",
68+
Type = "Secret"
69+
},
6370
["Google_ProjectId"] = new PropertyConfigInfo()
6471
{
6572
Comments = "Google Cloud DNS: Project ID only if using Google DNS (Optional)",
@@ -68,6 +75,15 @@ public static Dictionary<string, PropertyConfigInfo> GetPluginAnnotations()
6875
Type = "String"
6976
},
7077

78+
// Container Deployment
79+
["AccountStoragePath"] = new PropertyConfigInfo()
80+
{
81+
Comments = "Path for ACME account storage. Defaults to %APPDATA%\\AcmeAccounts on Windows or ./AcmeAccounts in containers.",
82+
Hidden = false,
83+
DefaultValue = "",
84+
Type = "String"
85+
},
86+
7187
// Cloudflare DNS
7288
["Cloudflare_ApiToken"] = new PropertyConfigInfo()
7389
{

AcmeCaPlugin/AcmeClientConfig.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class AcmeClientConfig
1515

1616
// Google Cloud DNS
1717
public string Google_ServiceAccountKeyPath { get; set; } = null;
18+
public string Google_ServiceAccountKeyJson { get; set; } = null;
1819
public string Google_ProjectId { get; set; } = null;
1920

2021
// Cloudflare DNS
@@ -34,5 +35,8 @@ public class AcmeClientConfig
3435
//IBM NS1 DNS Ns1_ApiKey
3536
public string Ns1_ApiKey { get; set; } = null;
3637

38+
// Container Deployment Support
39+
public string AccountStoragePath { get; set; } = null;
40+
3741
}
3842
}

AcmeCaPlugin/Clients/Acme/AccountManager.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,32 @@ class AccountManager
3939

4040
#region Constructor
4141

42-
public AccountManager(ILogger log, string passphrase = null)
42+
public AccountManager(ILogger log, string passphrase = null, string storagePath = null)
4343
{
4444
_log = log;
4545
_passphrase = passphrase;
46-
_basePath = Path.Combine(
47-
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
48-
"AcmeAccounts");
46+
47+
if (!string.IsNullOrWhiteSpace(storagePath))
48+
{
49+
// Use the explicitly configured path
50+
_basePath = storagePath;
51+
}
52+
else
53+
{
54+
// Default: Use platform-appropriate path
55+
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
56+
if (string.IsNullOrEmpty(appDataPath))
57+
{
58+
// In containers, APPDATA may not be set; use current directory
59+
_basePath = Path.Combine(Directory.GetCurrentDirectory(), "AcmeAccounts");
60+
}
61+
else
62+
{
63+
_basePath = Path.Combine(appDataPath, "AcmeAccounts");
64+
}
65+
}
66+
67+
_log.LogDebug("Account storage path configured: {BasePath}", _basePath);
4968
}
5069

5170
#endregion

AcmeCaPlugin/Clients/Acme/AcmeClientManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public AcmeClientManager(ILogger log, AcmeClientConfig config, HttpClient httpCl
6565
_email = config.Email;
6666
_eabKid = config.EabKid;
6767
_eabHmac = config.EabHmacKey;
68-
_accountManager = new AccountManager(log,config.SignerEncryptionPhrase);
68+
_accountManager = new AccountManager(log, config.SignerEncryptionPhrase, config.AccountStoragePath);
6969

7070
_log.LogDebug("AcmeClientManager initialized for directory: {DirectoryUrl}", _directoryUrl);
7171
}

AcmeCaPlugin/Clients/DNS/DnsProviderFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public static IDnsProvider Create(AcmeClientConfig config, ILogger logger)
1515
case "google":
1616
return new GoogleDnsProvider(
1717
config.Google_ServiceAccountKeyPath,
18+
config.Google_ServiceAccountKeyJson,
1819
config.Google_ProjectId
1920
);
2021

AcmeCaPlugin/Clients/DNS/GoogleDnsProvider.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
/// <summary>
1111
/// Google Cloud DNS provider implementation for managing DNS TXT records.
12-
/// Supports explicit Service Account key or Workload Identity (Application Default Credentials).
12+
/// Supports explicit Service Account key (file or JSON), or Workload Identity (Application Default Credentials).
1313
/// </summary>
1414
public class GoogleDnsProvider : IDnsProvider
1515
{
@@ -18,19 +18,26 @@ public class GoogleDnsProvider : IDnsProvider
1818

1919
/// <summary>
2020
/// Initializes a new instance of the GoogleDnsProvider class.
21-
/// If serviceAccountKeyPath is null or empty, uses Application Default Credentials.
21+
/// Credential resolution order: JSON key > File path > Application Default Credentials.
2222
/// </summary>
2323
/// <param name="serviceAccountKeyPath">Path to the Service Account JSON key file (optional)</param>
24+
/// <param name="serviceAccountKeyJson">Service Account JSON key as a string (optional, for containerized deployments)</param>
2425
/// <param name="projectId">Google Cloud project ID containing the DNS zones</param>
25-
public GoogleDnsProvider(string? serviceAccountKeyPath, string projectId)
26+
public GoogleDnsProvider(string? serviceAccountKeyPath, string? serviceAccountKeyJson, string projectId)
2627
{
2728
_projectId = projectId;
2829

2930
GoogleCredential credential;
3031

31-
if (!string.IsNullOrWhiteSpace(serviceAccountKeyPath))
32+
if (!string.IsNullOrWhiteSpace(serviceAccountKeyJson))
3233
{
33-
Console.WriteLine("✅ Using explicit Service Account JSON key.");
34+
// JSON key provided directly (for container deployments)
35+
Console.WriteLine("✅ Using Service Account JSON key from configuration.");
36+
credential = GoogleCredential.FromJson(serviceAccountKeyJson);
37+
}
38+
else if (!string.IsNullOrWhiteSpace(serviceAccountKeyPath))
39+
{
40+
Console.WriteLine("✅ Using Service Account JSON key from file.");
3441
credential = GoogleCredential.FromFile(serviceAccountKeyPath);
3542
}
3643
else

docsource/configuration.md

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ This plugin automates DNS-01 challenges using pluggable DNS provider implementat
6565

6666
| Provider | Auth Methods Supported | Config Keys Required |
6767
|--------------|-----------------------------------------------|--------------------------------------------------------|
68-
| Google DNS | Service Account Key or ADC | `Google_ServiceAccountKeyPath`, `Google_ProjectId` |
68+
| Google DNS | Service Account Key (file or JSON), or ADC | `Google_ServiceAccountKeyPath`, `Google_ServiceAccountKeyJson`, `Google_ProjectId` |
6969
| AWS Route 53 | Access Key/Secret or IAM Role | `AwsRoute53_AccessKey`, `AwsRoute53_SecretKey` |
7070
| Azure DNS | Client Secret or Managed Identity | `Azure_TenantId`, `Azure_ClientId`, `Azure_ClientSecret`, `Azure_SubscriptionId` |
7171
| Cloudflare | API Token | `Cloudflare_ApiToken` |
@@ -87,8 +87,9 @@ This logic is handled by the `DnsVerificationHelper` class and ensures a high-co
8787

8888
Each provider supports multiple credential strategies:
8989

90-
- **Google DNS**:
91-
-**Service Account Key** (via `Google_ServiceAccountKeyPath`)
90+
- **Google DNS**:
91+
-**Service Account Key File** (via `Google_ServiceAccountKeyPath`)
92+
-**Service Account Key JSON** (via `Google_ServiceAccountKeyJson` - paste JSON directly)
9293
-**Application Default Credentials** (e.g., GCP Workload Identity or developer auth)
9394

9495
- **AWS Route 53**:
@@ -229,12 +230,17 @@ This ACME Gateway implementation uses a local file-based store to persist ACME a
229230
<details>
230231
<summary><strong>📁 Account Directory Structure</strong></summary>
231232

232-
Each account is saved in its own directory within:
233+
Each account is saved in its own directory within the configured storage path:
233234

234235
```
235-
%APPDATA%\AcmeAccounts\{host}_{accountId}
236+
{AccountStoragePath}\{host}_{accountId}
236237
```
237238

239+
**Default paths:**
240+
- **Windows:** `%APPDATA%\AcmeAccounts\{host}_{accountId}`
241+
- **Containers (when APPDATA unavailable):** `./AcmeAccounts\{host}_{accountId}`
242+
- **Custom:** Set `AccountStoragePath` in the Gateway configuration
243+
238244
Where:
239245
- `{host}` is the ACME directory host with dots replaced by dashes (e.g., `acme-zerossl-com`)
240246
- `{accountId}` is the final segment of the account's KID URL
@@ -344,10 +350,10 @@ This section outlines all required ports, file access, permissions, and validati
344350

345351
| Path | Purpose |
346352
|----------------------------------------------------|----------------------------------------------|
347-
| `%APPDATA%\AcmeAccounts\` | Default base path for ACME account storage |
348-
| `AcmeAccounts\{account_id}\Registration_v2` | Contains serialized ACME account metadata |
349-
| `AcmeAccounts\{account_id}\Signer_v2` | Contains the encrypted private signer key |
350-
| `AcmeAccounts\default_{host}.txt` | Stores the default account pointer for a given directory |
353+
| `%APPDATA%\AcmeAccounts\` or `AccountStoragePath` | Base path for ACME account storage (configurable) |
354+
| `{base}\{account_id}\Registration_v2` | Contains serialized ACME account metadata |
355+
| `{base}\{account_id}\Signer_v2` | Contains the encrypted private signer key |
356+
| `{base}\default_{host}.txt` | Stores the default account pointer for a given directory |
351357

352358
#### File Access & Permissions
353359

@@ -357,7 +363,8 @@ This section outlines all required ports, file access, permissions, and validati
357363
| Account files | Read/Write| `Read`, `Write` |
358364

359365
- Files may be optionally encrypted using AES if a passphrase is configured.
360-
- Ensure the service account under which the orchestrator runs has read/write access to `%APPDATA%` or the custom configured base path.
366+
- Ensure the service account under which the orchestrator runs has read/write access to the configured base path.
367+
- For containers, mount a persistent volume to the `AccountStoragePath` to preserve accounts across restarts.
361368

362369
</details>
363370

@@ -384,6 +391,61 @@ This section outlines all required ports, file access, permissions, and validati
384391

385392
</details>
386393

394+
---
395+
396+
### Container Deployment
397+
398+
This section covers configuration options specific to containerized deployments (Docker, Kubernetes, etc.).
399+
400+
<details>
401+
<summary><strong>📁 Configurable Account Storage Path</strong></summary>
402+
403+
By default, the plugin stores ACME accounts in `%APPDATA%\AcmeAccounts` on Windows. In containerized environments, use the `AccountStoragePath` configuration option:
404+
405+
| Environment | Recommended Path |
406+
|-------------|------------------|
407+
| Docker/Kubernetes | `/data/AcmeAccounts` (mounted volume) |
408+
| Windows Container | `C:\AcmeData\AcmeAccounts` |
409+
410+
If `AccountStoragePath` is not set and `%APPDATA%` is unavailable, the plugin defaults to `./AcmeAccounts` relative to the working directory.
411+
412+
</details>
413+
414+
<details>
415+
<summary><strong>🌐 Google Cloud DNS in Containers</strong></summary>
416+
417+
For Google Cloud DNS in container environments, you have three authentication options:
418+
419+
1. **Workload Identity (GKE)**: No explicit credentials needed; uses pod identity.
420+
2. **JSON key in config**: Paste the service account JSON directly into `Google_ServiceAccountKeyJson`.
421+
3. **Mounted JSON file**: Mount the service account key file and set `Google_ServiceAccountKeyPath`.
422+
423+
</details>
424+
425+
<details>
426+
<summary><strong>☸️ Kubernetes Deployment Considerations</strong></summary>
427+
428+
When deploying in Kubernetes:
429+
430+
1. **Persistent Storage**: Use a PersistentVolumeClaim for `AccountStoragePath` to preserve ACME accounts across pod restarts.
431+
2. **Cloud Provider Identity**: Leverage Workload Identity (GKE), IAM Roles for Service Accounts (EKS), or Pod Identity (AKS) for DNS provider authentication.
432+
433+
**Example PersistentVolumeClaim:**
434+
```yaml
435+
apiVersion: v1
436+
kind: PersistentVolumeClaim
437+
metadata:
438+
name: acme-accounts
439+
spec:
440+
accessModes:
441+
- ReadWriteOnce
442+
resources:
443+
requests:
444+
storage: 100Mi
445+
```
446+
447+
</details>
448+
387449
388450
## Gateway Registration
389451

0 commit comments

Comments
 (0)