Rewrite to only consider Docker

1d995fb92d20a9ae5362e8eabf8c838cc01ddd00

Cohee <18619528+Cohee1207@users.noreply.github.com>

2 files changed, +19 -60Ignore whitespace
default/config.yaml+2 -1
@@ -42,7 +42,8 @@ enableForwardedWhitelist: true
42whitelist:42whitelist:
43 - ::143 - ::1
44 - 127.0.0.144 - 127.0.0.1
45 - gateway.docker.internal45# Automatically whitelist Docker host and gateway IPs
46whitelistDockerHosts: true
46# Toggle basic authentication for endpoints47# Toggle basic authentication for endpoints
47basicAuthMode: false48basicAuthMode: false
48# Basic authentication credentials49# Basic authentication credentials
src/middleware/whitelist.js+17 -59
@@ -3,14 +3,15 @@ import fs from 'node:fs';
3import process from 'node:process';3import process from 'node:process';
4import dns from 'node:dns';4import dns from 'node:dns';
5import Handlebars from 'handlebars';5import Handlebars from 'handlebars';
6import ipRegex from 'ip-regex';
7import ipMatching from 'ip-matching';6import ipMatching from 'ip-matching';
7import isDocker from 'is-docker';
88
9import { getIpFromRequest } from '../express-common.js';9import { getIpFromRequest } from '../express-common.js';
10import { color, getConfigValue, safeReadFileSync } from '../util.js';10import { color, getConfigValue, safeReadFileSync } from '../util.js';
1111
12const whitelistPath = path.join(process.cwd(), './whitelist.txt');12const whitelistPath = path.join(process.cwd(), './whitelist.txt');
13const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false, 'boolean');13const enableForwardedWhitelist = !!getConfigValue('enableForwardedWhitelist', false, 'boolean');
14const whitelistDockerHosts = !!getConfigValue('whitelistDockerHosts', false, 'boolean');
14/** @type {string[]} */15/** @type {string[]} */
15let whitelist = getConfigValue('whitelist', []);16let whitelist = getConfigValue('whitelist', []);
1617
@@ -49,68 +50,25 @@ function getForwardedIp(req) {
49}50}
5051
51/**52/**
52 * Checks if a string is a valid hostname according to RFC 1123
53 * @param {string} hostname The string to test
54 * @returns {boolean} True if the string is a valid hostname
55 */
56function isValidHostname(hostname) {
57 const hostnameRegex = /^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$/i;
58 return hostnameRegex.test(hostname);
59}
60
61/**
62 * Checks if a string is an IP address, CIDR notation, or IP wildcard
63 * @param {string} entry The string to test
64 * @returns {boolean} True if the string matches any IP format
65 */
66function isIpFormat(entry) {
67 // Match CIDR notation (e.g. 192.168.0.0/24)
68 if (entry.includes('/')) {
69 return true;
70 }
71
72 // Match exact IP address
73 if (ipRegex({ exact: true }).test(entry)) {
74 return true;
75 }
76
77 // Match IPv4 with wildcards (e.g. 192.168.*.* or 192.168.0.*)
78 const ipWildcardRegex = /^(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/;
79 return ipWildcardRegex.test(entry);
80}
81
82/**
83 * Resolves hostnames in the whitelist to IP addresses.53 * Resolves hostnames in the whitelist to IP addresses.
84 * This function will modify the whitelist array in place.54 * This function will modify the whitelist array in place.
85 */55 */
86async function resolveHostnames() {56async function addDockerHostsToWhitelist() {
87 const resolvedWhitelist = [];57 if (!whitelistDockerHosts || !isDocker()) {
58 return;
59 }
8860
89 const promises = whitelist.map(async (entry) => {61 const whitelistHosts = ['host.docker.internal', 'gateway.docker.internal'];
90 if (!entry || typeof entry !== 'string') {
91 return;
92 }
9362
94 // Skip if entry appears to be an IP address, CIDR notation, or IP wildcard63 for (const entry of whitelistHosts) {
95 if (isIpFormat(entry)) {64 try {
96 resolvedWhitelist.push(entry);65 const result = await dns.promises.lookup(entry);
97 return;66 console.info(`Resolved whitelist hostname ${color.green(entry)} to IPv${result.family} address ${color.green(result.address)}`);
67 whitelist.push(result.address);
68 } catch (e) {
69 console.warn(`Failed to resolve whitelist hostname ${color.red(entry)}: ${e.message}`);
98 }70 }
9971 }
100 if (isValidHostname(entry)) {
101 try {
102 const result = await dns.promises.lookup(entry);
103 console.info(`Resolved whitelist hostname ${color.green(entry)} to IPv${result.family} address ${color.green(result.address)}`);
104 resolvedWhitelist.push(result.address);
105 } catch (e) {
106 console.warn(`Failed to resolve whitelist hostname ${color.red(entry)}: ${e.message}`);
107 }
108 } else {
109 resolvedWhitelist.push(entry);
110 }
111 });
112
113 await Promise.allSettled(promises);
114}72}
11573
116/**74/**
@@ -126,7 +84,7 @@ export default async function getWhitelistMiddleware() {
126 '/favicon.ico',84 '/favicon.ico',
127 ];85 ];
12886
129 await resolveHostnames();87 await addDockerHostsToWhitelist();
13088
131 return function (req, res, next) {89 return function (req, res, next) {
132 const clientIp = getIpFromRequest(req);90 const clientIp = getIpFromRequest(req);