fix: (regression beta) acme certificate not working when not using port 80
Massimo Melina committed
Mar 28, 2024 at 14:51 UTC
b808302e773175c0ed5592a41d8adab2277fe790
1 file changed
+11
-21
src/acme.ts
+11
-21
@@ -1,16 +1,5 @@
1
-import {
2
- DAY,
3
- Dict,
4
- haveTimeout,
5
- HOUR,
6
- HTTP_BAD_REQUEST,
7
- HTTP_FAILED_DEPENDENCY,
8
- HTTP_OK,
9
- HTTP_SERVER_ERROR,
10
- MINUTE,
11
- repeat
12
-} from './cross'
13
-import { createServer, RequestListener } from 'http'
1
+import { DAY, Dict, haveTimeout, HOUR, HTTP_BAD_REQUEST, HTTP_FAILED_DEPENDENCY, HTTP_OK, MINUTE, repeat } from './misc'
2
+import { createServer, IncomingMessage, ServerResponse } from 'http'
3
import { Middleware } from 'koa'
4
import { getNatInfo, upnpClient } from './nat'
5
import { cert, getCertObject, getServerStatus, privateKey } from './listen'
@@ -22,19 +11,19 @@ import { defineConfig } from './config'
11
import events from './events'
12
import { selfCheck } from './selfCheck'
13
25
-let acmeMiddlewareEnabled = false
14
+let acmeOngoing = false
15
const acmeTokens: Dict<string> = {}
27
-const acmeListener: RequestListener = (req, res) => { // node format
16
+const acmeListener = (req: IncomingMessage, res: ServerResponse) => { // node listener
17
const BASE = '/.well-known/acme-challenge/'
18
if (!req.url?.startsWith(BASE)) return
19
const token = req.url.slice(BASE.length)
20
console.debug("got http challenge", token)
21
res.statusCode = HTTP_OK
22
res.end(acmeTokens[token])
34
- return true
23
+ return true // true = responded
24
}
25
export const acmeMiddleware: Middleware = (ctx, next) => { // koa format
37
- if (!acmeMiddlewareEnabled || !Boolean(acmeListener(ctx.req, ctx.res)))
26
+ if (!acmeOngoing || !acmeListener(ctx.req, ctx.res))
27
return next()
28
}
29
@@ -45,7 +34,7 @@ repeat(MINUTE, async stop => {
34
const res = await upnpClient.getMappings()
35
const leftover = res.find(x => x.description === TEMP_MAP.description) // in case the process is interrupted
36
if (!leftover) return void stop() // we are good
48
- if (acmeMiddlewareEnabled) return // it doesn't count, as we are in the middle of something. Retry later
37
+ if (acmeOngoing) return // it doesn't count, as we are in the middle of something. Retry later
38
stop()
39
return upnpClient.removeMapping(TEMP_MAP)
40
})
@@ -54,14 +43,15 @@ async function generateSSLCert(domain: string, email?: string, altNames?: string
43
// will answer challenge through our koa app (if on port 80) or must we spawn a dedicated server?
44
const nat = await getNatInfo()
45
const { http } = await getServerStatus()
57
- const tempSrv = nat.externalPort === 80 || http.listening && http.port === 80 ? undefined : createServer(acmeListener)
46
+ const tempSrv = nat.externalPort === 80 || http.listening && http.port === 80 ? undefined
47
+ : createServer((req, res) => acmeListener(req, res) || res.end('HFS')) // also satisfy self-check
48
if (tempSrv)
49
await new Promise<void>(resolve =>
50
tempSrv.listen(80, resolve).on('error', (e: any) => {
51
console.debug("cannot listen on 80", e.code || e)
52
resolve() // go on anyway
53
}) )
64
- acmeMiddlewareEnabled = true
54
+ acmeOngoing = true
55
console.debug("acme challenge server ready")
56
let tempMap: any
57
try {
@@ -98,7 +88,7 @@ async function generateSSLCert(domain: string, email?: string, altNames?: string
88
console.debug("removing temporary port forward")
89
upnpClient.removeMapping(TEMP_MAP).catch(() => {}) // clean after ourselves
90
}
101
- acmeMiddlewareEnabled = false
91
+ acmeOngoing = false
92
if (tempSrv) await new Promise(res => tempSrv.close(res))
93
console.debug('acme terminated')
94
}