Skip to content

Commit 7d7a0ec

Browse files
committed
Move ACME setup to pure PK config, no EAB, since Google requires it
1 parent 61dcf16 commit 7d7a0ec

3 files changed

Lines changed: 20 additions & 25 deletions

File tree

fly.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ primary_region = 'cdg'
1515
PROACTIVE_CERT_DOMAINS = 'testserver.host,example.testserver.host,http1.testserver.host,http2.testserver.host'
1616

1717
ACME_PROVIDER = 'google' # or 'letsencrypt', 'zerossl'
18-
# For google/zerossl you'll need to set ACME_EAB_HMAC and ACME_EAB_KID secrets for auth
18+
# Set ACME_ACCOUNT_KEY secret (PEM format) - see src/tls-certificates/acme.ts for details
1919

2020
[[services]]
2121
protocol = "tcp"

src/server.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { createHttp1Handler, createHttp2Handler } from './http-handler.js';
1111
import { createTlsHandler, CertMode } from './tls-handler.js';
1212
import { ConnectionProcessor } from './process-connection.js';
1313

14-
import { AcmeCA, AcmeProvider, ExternalAccessBindingConfig } from './tls-certificates/acme.js';
14+
import { AcmeCA, AcmeProvider } from './tls-certificates/acme.js';
1515
import { LocalCA, generateCACertificate } from './tls-certificates/local-ca.js';
1616
import { PersistentCertCache } from './tls-certificates/cert-cache.js';
1717

@@ -32,9 +32,9 @@ declare module 'stream' {
3232
interface ServerOptions {
3333
domain?: string;
3434
acmeProvider?: AcmeProvider;
35+
acmeAccountKey?: string;
3536
proactiveCertDomains?: string[];
3637
certCacheDir?: string;
37-
eabConfig?: ExternalAccessBindingConfig;
3838
}
3939

4040
async function generateTlsConfig(options: ServerOptions) {
@@ -79,8 +79,11 @@ async function generateTlsConfig(options: ServerOptions) {
7979
if (!options.certCacheDir || !AcmeCA) {
8080
throw new Error(`Can't enable ACME without configuring a cert cache directory (via $CERT_CACHE_DIR)`);
8181
}
82+
if (!options.acmeAccountKey) {
83+
throw new Error(`Can't enable ACME without configuring an account key (via $ACME_ACCOUNT_KEY)`);
84+
}
8285

83-
const acmeCA = new AcmeCA(certCache!, options.acmeProvider, options.eabConfig);
86+
const acmeCA = new AcmeCA(certCache!, options.acmeProvider, options.acmeAccountKey);
8487
acmeCA.tryGetCertificateSync(rootDomain); // Preload the root domain every time
8588

8689
return {
@@ -182,9 +185,7 @@ if (wasRunDirectly) {
182185
domain: process.env.ROOT_DOMAIN,
183186
proactiveCertDomains: process.env.PROACTIVE_CERT_DOMAINS?.split(','),
184187
acmeProvider: process.env.ACME_PROVIDER as AcmeProvider | undefined,
185-
eabConfig: process.env.ACME_EAB_KID && process.env.ACME_EAB_HMAC
186-
? { kid: process.env.ACME_EAB_KID, hmacKey: process.env.ACME_EAB_HMAC }
187-
: undefined,
188+
acmeAccountKey: process.env.ACME_ACCOUNT_KEY,
188189
certCacheDir: process.env.CERT_CACHE_DIR
189190
}).then((tcpHandler) => {
190191
ports.forEach((port) => {

src/tls-certificates/acme.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,30 @@ const SUPPORTED_ACME_PROVIDERS = [
2222

2323
export type AcmeProvider = typeof SUPPORTED_ACME_PROVIDERS[number];
2424

25-
export interface ExternalAccessBindingConfig {
26-
kid: string;
27-
hmacKey: string;
28-
}
29-
3025
export class AcmeCA {
3126

27+
private readonly acmeClient: ACME.Client;
28+
3229
constructor(
3330
private certCache: PersistentCertCache,
3431
private acmeProvider: AcmeProvider,
35-
private eabConfig: ExternalAccessBindingConfig | undefined
32+
accountKey: string
3633
) {
3734
if (!SUPPORTED_ACME_PROVIDERS.includes(acmeProvider)) {
3835
throw new Error(`Unsupported ACME provider: ${acmeProvider}`);
3936
}
37+
38+
this.acmeClient = new ACME.Client({
39+
directoryUrl: ACME.directory[this.acmeProvider].production,
40+
accountKey
41+
});
4042
}
4143

4244
private pendingAcmeChallenges: { [token: string]: string | undefined } = {}
4345
private pendingCertRenewals: { [domain: string]: (Promise<AcmeGeneratedCertificate> & { id: string }) | undefined } = {};
4446

45-
private readonly acmeClient = ACME.crypto.createPrivateKey().then(
46-
(accountKey) => new ACME.Client({
47-
directoryUrl: ACME.directory[this.acmeProvider].production,
48-
accountKey,
49-
externalAccountBinding: this.eabConfig
50-
})
51-
);
52-
53-
async getChallengeResponse(token: string) {
54-
const challengeResponse = (await this.acmeClient).getChallengeKeyAuthorization({
47+
getChallengeResponse(token: string) {
48+
const challengeResponse = this.acmeClient.getChallengeKeyAuthorization({
5549
token,
5650
type: 'http-01',
5751
url: '',
@@ -127,7 +121,7 @@ export class AcmeCA {
127121
const certData = await this.requestNewCertificate(domain, options);
128122

129123
console.log(`Revoking certificate for ${domain} (${options.attemptId})`);
130-
await (await this.acmeClient).revokeCertificate(certData.cert);
124+
await this.acmeClient.revokeCertificate(certData.cert);
131125
console.log(`Certificate revoked for ${domain} (${options.attemptId})`);
132126

133127
return certData;
@@ -270,7 +264,7 @@ export class AcmeCA {
270264
commonName: domain
271265
});
272266

273-
const cert = await (await this.acmeClient).auto({
267+
const cert = await this.acmeClient.auto({
274268
csr,
275269
challengePriority: ['http-01'],
276270
termsOfServiceAgreed: true,

0 commit comments

Comments
 (0)