From 5116586d79df7216b124e74715f6414ffffa7e3a Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 13 May 2025 22:19:24 -0400 Subject: [PATCH] improve YAML syntax for defining allowed IPs --- .config/ci.yml | 21 ++++++++++++-- .config/cypress-devcontainer.yml | 24 +++++++++++++-- .config/docker_example.yml | 21 ++++++++++++-- .config/example.yml | 21 ++++++++++++-- packages/backend/src/config.ts | 50 ++++++++++++++++++++++++-------- 5 files changed, 113 insertions(+), 24 deletions(-) diff --git a/.config/ci.yml b/.config/ci.yml index fefa45643c..4a6d21e1d5 100644 --- a/.config/ci.yml +++ b/.config/ci.yml @@ -321,9 +321,24 @@ attachLdSignatureForRelays: true # For security reasons, uploading attachments from the intranet is prohibited, # but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). -#allowedPrivateNetworks: [ -# '127.0.0.1/32' -#] +# Some example configurations: +#allowedPrivateNetworks: +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1/32' +# # Allow connections to 127.0.0.* on any port +# - '127.0.0.1/24' +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1' +# # Allow connections to 127.0.0.1 on any port +# - network: '127.0.0.1' +# # Allow connections to 127.0.0.1 on port 80 +# - network: '127.0.0.1' +# ports: [80] +# # Allow connections to 127.0.0.1 on port 80 or 443 +# - network: '127.0.0.1' +# ports: +# - 80 +# - 443 #customMOTD: ['Hello World', 'The sharks rule all', 'Shonks'] diff --git a/.config/cypress-devcontainer.yml b/.config/cypress-devcontainer.yml index e4eb8cc805..356d583611 100644 --- a/.config/cypress-devcontainer.yml +++ b/.config/cypress-devcontainer.yml @@ -269,9 +269,27 @@ proxyRemoteFiles: true # Sign to ActivityPub GET request (default: true) signToActivityPubGet: true -allowedPrivateNetworks: [ - '127.0.0.1/32' -] +# For security reasons, uploading attachments from the intranet is prohibited, +# but exceptions can be made from the following settings. Default value is "undefined". +# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). +# Some example configurations: +allowedPrivateNetworks: + # Allow connections to 127.0.0.1 on any port + - '127.0.0.1/32' +# # Allow connections to 127.0.0.* on any port +# - '127.0.0.1/24' +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1' +# # Allow connections to 127.0.0.1 on any port +# - network: '127.0.0.1' +# # Allow connections to 127.0.0.1 on port 80 +# - network: '127.0.0.1' +# ports: [80] +# # Allow connections to 127.0.0.1 on port 80 or 443 +# - network: '127.0.0.1' +# ports: +# - 80 +# - 443 # Disable automatic redirect for ActivityPub object lookup. (default: false) # This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation. diff --git a/.config/docker_example.yml b/.config/docker_example.yml index 7968a7d1f4..68679f64ed 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -378,9 +378,24 @@ attachLdSignatureForRelays: true # For security reasons, uploading attachments from the intranet is prohibited, # but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). -#allowedPrivateNetworks: [ -# '127.0.0.1/32' -#] +# Some example configurations: +#allowedPrivateNetworks: +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1/32' +# # Allow connections to 127.0.0.* on any port +# - '127.0.0.1/24' +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1' +# # Allow connections to 127.0.0.1 on any port +# - network: '127.0.0.1' +# # Allow connections to 127.0.0.1 on port 80 +# - network: '127.0.0.1' +# ports: [80] +# # Allow connections to 127.0.0.1 on port 80 or 443 +# - network: '127.0.0.1' +# ports: +# - 80 +# - 443 #customMOTD: ['Hello World', 'The sharks rule all', 'Shonks'] diff --git a/.config/example.yml b/.config/example.yml index d0ed4defaa..9cb1e656c1 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -381,9 +381,24 @@ attachLdSignatureForRelays: true # For security reasons, uploading attachments from the intranet is prohibited, # but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). -#allowedPrivateNetworks: [ -# '127.0.0.1/32' -#] +# Some example configurations: +#allowedPrivateNetworks: +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1/32' +# # Allow connections to 127.0.0.* on any port +# - '127.0.0.1/24' +# # Allow connections to 127.0.0.1 on any port +# - '127.0.0.1' +# # Allow connections to 127.0.0.1 on any port +# - network: '127.0.0.1' +# # Allow connections to 127.0.0.1 on port 80 +# - network: '127.0.0.1' +# ports: [80] +# # Allow connections to 127.0.0.1 on port 80 or 443 +# - network: '127.0.0.1' +# ports: +# - 80 +# - 443 #customMOTD: ['Hello World', 'The sharks rule all', 'Shonks'] diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 2a3184f9b4..9725bcc367 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -84,7 +84,7 @@ type Source = { proxySmtp?: string; proxyBypassHosts?: string[]; - allowedPrivateNetworks?: string[]; + allowedPrivateNetworks?: PrivateNetworkSource[]; disallowExternalApRedirect?: boolean; maxFileSize?: number; @@ -154,11 +154,13 @@ type Source = { } }; +export type PrivateNetworkSource = string | { ip?: string, ports?: number[] }; + export type PrivateNetwork = { /** * CIDR IP/netmask definition of the IP range to match. */ - cidr: [ip: IPv4 | IPv6, mask: number]; + cidr: CIDR; /** * List of ports to match. @@ -168,17 +170,41 @@ export type PrivateNetwork = { ports?: number[]; }; -export function parsePrivateNetworks(patterns: string[]): PrivateNetwork[]; +export type CIDR = [ip: IPv4 | IPv6, mask: number]; + +export function parsePrivateNetworks(patterns: PrivateNetworkSource[]): PrivateNetwork[]; export function parsePrivateNetworks(patterns: undefined): undefined; -export function parsePrivateNetworks(patterns: string[] | undefined): PrivateNetwork[] | undefined; -export function parsePrivateNetworks(patterns: string[] | undefined): PrivateNetwork[] | undefined { - return patterns?.map(e => { - const [ip, ports] = e.split('#') as [string, ...(string | undefined)[]]; - return { - cidr: ipaddr.parseCIDR(ip), - ports: ports?.split(',').map(p => parseInt(p)), - }; - }); +export function parsePrivateNetworks(patterns: PrivateNetworkSource[] | undefined): PrivateNetwork[] | undefined; +export function parsePrivateNetworks(patterns: PrivateNetworkSource[] | undefined): PrivateNetwork[] | undefined { + if (!patterns) return undefined; + return patterns + .map(e => { + if (typeof(e) === 'string') { + const cidr = parseIpOrMask(e); + if (cidr) { + return { cidr } satisfies PrivateNetwork; + } + } else if (e.ip) { + const cidr = parseIpOrMask(e.ip); + if (cidr) { + return { cidr, ports: e.ports } satisfies PrivateNetwork; + } + } + + console.warn('[config] Skipping invalid entry in allowedPrivateNetworks: ', e); + return null; + }) + .filter(p => p != null); +} + +function parseIpOrMask(ipOrMask: string): CIDR | null { + if (ipaddr.isValidCIDR(ipOrMask)) { + return ipaddr.parseCIDR(ipOrMask); + } + if (ipaddr.isValid(ipOrMask)) { + return ipaddr.parseCIDR(ipOrMask); + } + return null; } export type Config = {