domain check now uses external DNS server https://github.com/rejetto/hfs/discussions/346#discussioncomment-7024128

Massimo Melina committed Sep 17, 2023 at 11:47 UTC 0c4e6d3c0dc68cc22eea22ab5e9b89aeee829b6d
3 files changed +19 -9
central.json
+1
@@ -1,4 +1,5 @@
1 {
2 + "dnsServers": ["1.1.1.1", "8.8.8.8"],
3 "checkServerServices": [
4 {
5 "url": "https://ports.yougetsignal.com/check-port.php",
src/api.net.ts
+16 -7
@@ -13,13 +13,13 @@ import { cert, getCertObject, getIps, getServerStatus, privateKey } from './list
13 import { getProjectInfo } from './github'
14 import { httpString } from './util-http'
15 import { exec } from 'child_process'
16 -import { apiAssertTypes, debounceAsync, HOUR, MINUTE, objSameKeys, repeat } from './misc'
16 +import { apiAssertTypes, debounceAsync, HOUR, MINUTE, objSameKeys, onlyTruthy, repeat } from './misc'
17 import acme from 'acme-client'
18 import fs from 'fs/promises'
19 import { Dict } from './misc'
20 import { createServer, RequestListener } from 'http'
21 import { Middleware } from 'koa'
22 -import { lookup } from 'dns/promises'
22 +import { lookup, Resolver } from 'dns/promises'
23
24 const client = new Client({ timeout: 4_000 })
25 const originalMethod = client.getGateway
@@ -95,12 +95,21 @@ export const acmeMiddleware: Middleware = (ctx, next) => { // koa format
95 }
96
97 async function checkDomain(domain: string) {
98 - const { address: domainIp } = await lookup(domain).catch(e => {
99 - throw e.code !== 'ENOTFOUND' ? e : new ApiError(HTTP_FAILED_DEPENDENCY, "this domain doesn't exist")
100 - })
98 + const resolver = new Resolver()
99 + const prjInfo = await getProjectInfo()
100 + resolver.setServers(prjInfo.dnsServers)
101 + const settled = await Promise.allSettled([
102 + resolver.resolve(domain, 'A'),
103 + resolver.resolve(domain, 'AAAA'),
104 + lookup(domain).then(x => [x.address]),
105 + ])
106 + // merge all results
107 + const ips = _.uniq(onlyTruthy(settled.map(x => x.status === 'fulfilled' && x.value)).flat())
108 + if (!ips.length)
109 + throw new ApiError(HTTP_FAILED_DEPENDENCY, "domain not working")
110 const { publicIp } = await getNatInfo() // do this before stopping the server
102 - if (publicIp !== domainIp)
103 - throw new ApiError(HTTP_FAILED_DEPENDENCY, `please configure your domain to point to ${publicIp} (currently on ${domainIp}) --- a change can take hours to be effective`)
111 + if (!ips.includes(publicIp))
112 + throw new ApiError(HTTP_FAILED_DEPENDENCY, `please configure your domain to point to ${publicIp} (currently on ${ips[0]}) --- a change can take hours to be effective`)
113 }
114
115 async function generateSSLCert(domain: string, email?: string) {
src/misc.ts
+2 -2
@@ -56,8 +56,8 @@ export function onOff(em: EventEmitter, events: { [eventName:string]: (...args:
56 }
57 }
58
59 -export function isLocalHost(c: Connection | Koa.Context) {
60 - const ip = c.socket.remoteAddress // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
59 +export function isLocalHost(c: Connection | Koa.Context | string) {
60 + const ip = typeof c === 'string' ? c : c.socket.remoteAddress // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
61 return ip && (ip === '::1' || ip.endsWith('127.0.0.1'))
62 }
63