e7098015de
- Add package.json for project metadata and dependencies - Implement CLI in src/cli.ts for managing SSL/TLS certificates - Create Azure Functions host configuration in src/function/host.json - Set up timer function in src/function/index.ts for scheduled certificate management - Define configuration loading and error handling in src/lib/config.ts - Implement DNS zone scanning and challenge management in src/lib/dns.ts - Develop ACME client for certificate issuance in src/lib/acme.ts - Create KeyVault store for managing secrets and certificates in src/lib/keyvault.ts - Implement provisioning logic in src/lib/provisioner.ts for issuing and renewing certificates - Add TypeScript configuration files for building the project
148 lines
6.9 KiB
Markdown
148 lines
6.9 KiB
Markdown
# Azure ACME Provisioner
|
|
|
|
Azure ACME Provisioner is a NodeJS package that provides necessary tools to automate the process of obtaining SSL/TLS certificates from ACME (Automatic Certificate Management Environment) compliant certificate authorities, such as Let's Encrypt, for applications hosted on Microsoft Azure. It uses Azure KeyVault to securely store and manage the obtained certificates and ACME account credentials. The package may function as a standalone tool, a docker image, as a library or as an Azure Function, making it versatile for various deployment scenarios.
|
|
|
|
## Features
|
|
|
|
- Uses ACME protocol to automate certificate issuance and renewal.
|
|
- Stores ACME account information as secrets in Azure KeyVault for secure management.
|
|
- Stores obtained SSL/TLS certificates in Azure KeyVault for easy access and management.
|
|
- Automatically scans configured Azure DNS zones to identify records that require certificates (uses the `acme` tag to identify relevant recordsets).
|
|
|
|
## Requirements
|
|
|
|
- Node.js 24 or later
|
|
- Azure subscription with:
|
|
- Azure DNS zone(s) with records tagged `acme: true` or `acme: enabled`
|
|
- Azure Key Vault instance
|
|
- Managed Identity (or service principal) with permissions to read/write Key Vault secrets and certificates, and to manage DNS record sets
|
|
|
|
## Installation
|
|
|
|
```sh
|
|
npm install azure-acme-provisioner
|
|
```
|
|
|
|
Or use the CLI directly via `npx`:
|
|
|
|
```sh
|
|
npx azure-acme-provisioner --help
|
|
```
|
|
|
|
## DNS Zone Tagging
|
|
|
|
The provisioner discovers domains by scanning Azure DNS zones. Tag a **zone** or individual **A/CNAME recordsets** with `acme: true` to include them:
|
|
|
|
- **Zone-level tag** — issues certificates for both the zone apex (`example.com`) and a wildcard (`*.example.com`) as a single SAN order.
|
|
- **Recordset-level tag** — issues a certificate for that specific FQDN.
|
|
|
|
## CLI Usage
|
|
|
|
```
|
|
Commands:
|
|
run Scan DNS zones and issue or renew certificates (default)
|
|
scan List all domains tagged for ACME management
|
|
status Show certificate expiry status for all managed domains
|
|
renew Force-renew a certificate for a specific domain
|
|
|
|
Common options:
|
|
--keyvault-url <url> Azure KeyVault URL
|
|
--subscription-id <id> Azure subscription ID
|
|
--resource-group <rg> Resource group to scan (repeatable)
|
|
--dns-zone <zone> Restrict to specific DNS zone (repeatable)
|
|
--email <email> ACME contact email
|
|
--renewal-threshold <days> Days before expiry to renew (default: 30)
|
|
--dry-run Show what would be done without making changes
|
|
--log-level <level> debug | info | warn | error (default: info)
|
|
--output <format> table | json (scan and status commands)
|
|
```
|
|
|
|
## Configuration
|
|
|
|
All configuration is via environment variables. CLI flags override env vars when both are provided.
|
|
|
|
### Required
|
|
|
|
| Variable | Description |
|
|
|---|---|
|
|
| `ACME_KEYVAULT_URL` | Azure Key Vault URL, e.g. `https://myvault.vault.azure.net` |
|
|
| `ACME_SUBSCRIPTION_ID` | Azure subscription ID |
|
|
| `ACME_RESOURCE_GROUPS` | Comma-separated list of resource groups to scan |
|
|
| `ACME_CONTACT_EMAIL` | Contact email registered with the ACME CA |
|
|
|
|
### Optional
|
|
|
|
| Variable | Default | Description |
|
|
|---|---|---|
|
|
| `ACME_DNS_ZONES` | all zones in resource groups | Comma-separated list of DNS zone names to restrict scanning |
|
|
| `ACME_DIRECTORY_URL` | Let's Encrypt production | ACME directory URL |
|
|
| `ACME_RENEWAL_THRESHOLD_DAYS` | `30` | Renew certificates this many days before expiry |
|
|
| `ACME_DNS_PROPAGATION_WAIT` | `60` | Maximum seconds to wait for DNS TXT record propagation |
|
|
| `ACME_DNS_CHALLENGE_TTL` | `60` | TTL (seconds) for DNS-01 challenge TXT records |
|
|
| `ACME_LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
|
|
|
|
### Azure Authentication
|
|
|
|
The provisioner uses [`DefaultAzureCredential`](https://learn.microsoft.com/en-us/javascript/api/@azure/identity/defaultazurecredential) from `@azure/identity`, which tries authentication methods in this order:
|
|
|
|
1. **Managed Identity** — recommended for Azure-hosted deployments (Functions, ACI, AKS). Assign a system or user-assigned managed identity with the required RBAC roles. No credential configuration needed.
|
|
2. **Workload Identity Federation** — for Kubernetes or CI/CD (GitHub Actions). Set `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and `AZURE_FEDERATED_TOKEN_FILE`. No secrets required.
|
|
3. **Certificate-based service principal** — set `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and `AZURE_CLIENT_CERTIFICATE_PATH` (optionally `AZURE_CLIENT_CERTIFICATE_PASSWORD`, `AZURE_CLIENT_SEND_CERTIFICATE_CHAIN`).
|
|
4. **Client secret service principal** — set `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, and `AZURE_CLIENT_SECRET`. Least secure; use only as a last resort.
|
|
5. **Azure CLI / Developer CLI** — used automatically in local development when logged in via `az login` or `azd auth login`.
|
|
|
|
For sovereign clouds (Azure Government, Azure China), set `AZURE_AUTHORITY_HOST` to the appropriate authority endpoint.
|
|
|
|
## Azure Function
|
|
|
|
The package includes an Azure Functions v4 timer trigger that runs the provisioner daily at 02:00 UTC. To deploy, point the function app at this package's entry point and configure the environment variables above as application settings.
|
|
|
|
The function app requires a Managed Identity with the following RBAC assignments:
|
|
|
|
| Scope | Role |
|
|
|---|---|
|
|
| Key Vault | Key Vault Certificates Officer |
|
|
| Key Vault | Key Vault Secrets Officer |
|
|
| DNS Zone(s) | DNS Zone Contributor |
|
|
|
|
## Docker
|
|
|
|
```sh
|
|
docker run --rm \
|
|
-e ACME_KEYVAULT_URL=https://myvault.vault.azure.net \
|
|
-e ACME_SUBSCRIPTION_ID=<subscription-id> \
|
|
-e ACME_RESOURCE_GROUPS=my-rg \
|
|
-e ACME_CONTACT_EMAIL=admin@example.com \
|
|
ghcr.io/your-org/azure-acme-provisioner
|
|
```
|
|
|
|
When running in Azure Container Instances with a user-assigned Managed Identity, set `AZURE_CLIENT_ID` to the identity's client ID. No other credential variables are needed.
|
|
|
|
## Library Usage
|
|
|
|
```typescript
|
|
import { Provisioner, loadConfig } from 'azure-acme-provisioner';
|
|
|
|
const config = loadConfig(); // reads from environment variables
|
|
const provisioner = new Provisioner(config);
|
|
const result = await provisioner.run();
|
|
console.log(result);
|
|
```
|
|
|
|
## Certificate Storage
|
|
|
|
Certificates are stored as native Azure Key Vault Certificates (PEM format, `application/x-pem-file`), making them available to Azure App Service, API Management, and other Azure services that integrate with Key Vault.
|
|
|
|
ACME account credentials (private key and account URL) are stored as Key Vault Secrets and reused across runs.
|
|
|
|
Certificate names are derived from the domain: dots are replaced with hyphens, wildcards become `wildcard-`, and a `cert-` prefix is added. For example:
|
|
|
|
| Domain | Key Vault certificate name |
|
|
|---|---|
|
|
| `api.example.com` | `cert-api-example-com` |
|
|
| `*.example.com` | `cert-wildcard-example-com` |
|
|
|
|
## AI Disclaimer
|
|
|
|
The files in this repository may contain code generated by AI tools. While we strive to ensure the quality and security of all code, we recommend reviewing any AI-generated code before using it in production environments.
|