feat: initialize azure-acme-provisioner project with core functionality
- 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
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { TokenCredential } from '@azure/identity';
|
||||
import {
|
||||
CertificateClient,
|
||||
ImportCertificateOptions,
|
||||
KeyVaultCertificateWithPolicy,
|
||||
} from '@azure/keyvault-certificates';
|
||||
import { SecretClient } from '@azure/keyvault-secrets';
|
||||
|
||||
export class KeyVaultStore {
|
||||
private readonly secretClient: SecretClient;
|
||||
private readonly certClient: CertificateClient;
|
||||
|
||||
constructor(credential: TokenCredential, keyVaultUrl: string) {
|
||||
this.secretClient = new SecretClient(keyVaultUrl, credential);
|
||||
this.certClient = new CertificateClient(keyVaultUrl, credential);
|
||||
}
|
||||
|
||||
async getSecret(name: string): Promise<string | undefined> {
|
||||
try {
|
||||
const secret = await this.secretClient.getSecret(name);
|
||||
return secret.value;
|
||||
} catch (err: unknown) {
|
||||
if (isNotFound(err)) return undefined;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async setSecret(name: string, value: string): Promise<void> {
|
||||
await this.secretClient.setSecret(name, value);
|
||||
}
|
||||
|
||||
async getCertificate(name: string): Promise<KeyVaultCertificateWithPolicy | undefined> {
|
||||
try {
|
||||
return await this.certClient.getCertificate(name);
|
||||
} catch (err: unknown) {
|
||||
if (isNotFound(err)) return undefined;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async certificateExpiresWithin(name: string, days: number): Promise<boolean | 'missing'> {
|
||||
const cert = await this.getCertificate(name);
|
||||
if (!cert) return 'missing';
|
||||
const expiresOn = cert.properties.expiresOn;
|
||||
if (!expiresOn) return false;
|
||||
const thresholdMs = days * 24 * 60 * 60 * 1000;
|
||||
return expiresOn.getTime() - Date.now() <= thresholdMs;
|
||||
}
|
||||
|
||||
async importCertificate(name: string, pemBundle: string): Promise<void> {
|
||||
const options: ImportCertificateOptions = {
|
||||
policy: {
|
||||
contentType: 'application/x-pem-file',
|
||||
issuerName: 'Unknown',
|
||||
subject: 'CN=unknown',
|
||||
},
|
||||
};
|
||||
await this.certClient.importCertificate(name, Buffer.from(pemBundle), options);
|
||||
}
|
||||
}
|
||||
|
||||
function isNotFound(err: unknown): boolean {
|
||||
return (
|
||||
typeof err === 'object' &&
|
||||
err !== null &&
|
||||
'statusCode' in err &&
|
||||
(err as { statusCode: number }).statusCode === 404
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user